IBM Cognos 最佳实践: 在 IBM Cognos Report Studio 中实现自动级联提示而不刷新页面

文档性质:技巧或技术;产品;IBM Cognos Report Studio;关注领域:报表

本文档将介绍实现自动级联值提示而不用刷新页面的技术。

Amit Shivde, 系统分析师, Cognizant Technology Solutions

Amit Shivde 是 Cognizant Technology Solutions 的系统分析员。他从事 Data Warehousing 项目已有超过 3 年半的时间。他深入研究用于复杂报表的 Cognos 工具,并根据每个客户的业务需求为他们提供解决方案。



2012 年 4 月 16 日

免费下载:IBM® Cognos® Express V9.5 或者 Cognos® 8 Business Intelligence Developer Edition V8.4 试用版
下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。

简介

目的

本文档将介绍实现自动级联值提示而不用刷新页面的技术。

适用范围

本技术已经在 IBM Cognos 8.2、IBM Cognos 8.4.0、IBM Cognos 8.4.1 和 IBM Cognos 10.1 Report Studio 版本中进行测试。本技术已经通用 JavaScript 实现。

例外情况

本技术在 Internet Explorer 5.0 及以上版本中运行正常。在 Firefox 中不可用。


自动级联提示

报表应用程序通常需要使用提示值级联,以限制数据范围并降低报表输出中获取不到数据的机率。本地的 IBM Cognos 函数提供了 ‘单选值提示’ 或 ‘多选值提示的重新提示按钮’ 的自动级联功能,该功能在每次选择提示时,都会刷新整个提示选择页。因此,如果提示的数目很多,那么这种整个页面刷新的方法就会非常耗时,而且会令终端用户反感。

实现不刷新页面的自动级联的步骤:

  1. 在需要级联的提示页中加上选定的值提示。在本示例中,添加了Product Line、Product Type 和 Product Name 提示。Product Type 根据 Product Line 级联,Product Name 根据 Product Line 和 Product Type 级联。每个提示框都有自己的查询。
    带有 Product Line、Product Type 和 Product Name 的 Report Studio 提示页
    带有 Product Line、Product Type 和 Product Name 的 Report Studio 提示页
  2. 为每个提示命名。在本示例中,Prompt Names 以 “PN_” 开始。给每个提示赋予惟一的名称,这对于使用 JavaScript 访问这些提示是至关重要的。将 Product Line 提示命名为 PN_ProductLine,将 Product Type 命名为 PN_ProductType,并将 Product Name 命名为 PN_Product。在 Value Prompt 属性中的 Miscellaneous → Name 下输入名称。
  3. 创建一个新的查询,该查询要包含所有提示查询条目的连接值。在本示例中,已创建了一个 Q_Prompt 查询,该查询包含了一个名为 “Concat String” 的新数据项表达式 (Data Item Expression),该表达式会将三个提示值连接成字符串。请确保在连接字符串时,每个值之间使用的是不同的标识符。
    表达式如下所示:
    '!'|| [gosales_goretailers].[Products].[Product line] || '~'|| 
    [gosales_goretailers].[Products].[Product type] || '#' || 
    [gosales_goretailers].[Products].[Product name] || '^'
    数据项表达式窗口
    数据项表达式窗口
    注意:如果使用和显示的值不同,那么应使用显示值进行连接。
  4. 在提示页面,在提示后插入一个 HTML 项,在本例中将它称为 ‘fW Script’,然后插入以下代码:
    <script>
    var fW = (typeof getFormWarpRequest == "function" ?getFormWarpRequest() : 
       document.forms["formWarpRequest"]);
    if ( !fW || fW == undefined) { fW = ( formWarpRequest_THIS_ ?formWarpRequest_THIS_ : 
       formWarpRequest_NS_ );}
    </script>
  5. 在提示页中,在第一个 HTML 项后再插入一个 HTML 项。将它称为 ‘GetAllPromptNames’,然后插入以下代码:
    <script>
    //------------------------------------get All Prompt Names----------------------
    function getAllPromptNames()
    {
    
    var aPromptNames = new Array();
    
    for (var i in window) {
    if ( (""+i).match(/^listBox/) ) {
    var sName = i.replace( /^listBox/, "" );
    aPromptNames.push (sName);
    }
    else if ( (""+i).match(/^radioList/) ){
    
    var sName = i;
    aPromptNames.push (sName);
    }
    else if ( (""+i).match(/^checkBoxList/) ){
    
    var sName = i;
    aPromptNames.push (sName);
    }
    }
    return aPromptNames;
    //-----------------------------------to avoid Memory Leak----
    	i=null;
    	sName=null
    
    }
    //------------------------------------get All Prompt Names----------------------
    
    // hiding select All/deselect All 
    for(j=0;j<document.links.length;j++)
    {
    y=document.links[j];
    if (y.id.indexOf("SELECT") != -1) document.getElementById(y.id).style.display='None';
    }
    </script>
  6. 在提示页,在之前插入的 HTML 项之后插入第三段 HTML 项,然后插入以下代码:
    <script>
    String.prototype.trim = function() {
    	return this.replace(/^\s+|\s+$/g,"");
    }
     fW._oLstChoicesPN_ProductLine.onchange = function ()
          {cascadePrompts("ProductLine","ProductType,Product")};
     fW._oLstChoicesPN_ProductType.onchange = function ()
          {cascadePrompts("ProductType","Product")};
    
    //Check if the prompts being cascaded are present 
    // on page or removed as per report requirement
    function validatePrompts(removearr)
    {
    promptsonpage=getAllPromptNames();
    	var flag ;
    	newarr = new Array();
    	var i=0;
    	for(var j =0 ; j < removearr.length ; j++ )
    		{
    			flag = false;
    			for (var jj =0 ; jj < promptsonpage.length ; jj++ ){
    				
    				if(("PN_"+removearr[j])==promptsonpage[jj])
    					{
    						flag=true;
    						break;
    					}
    			}
    			if(flag==true )
    				{
    					newarr[i]=removearr[j];
    					i++;
    				}
    		}
    	return newarr;
    }
    
    //calls all the functions required for cascadeing
    	// prompt:Listprompt on which has the values to filter the child prompts
    	//remove:Child prompts on which the cascading is supposed to be performed
    function cascadePrompts(prompt,remove){
    	removearr = new Array();
    	removearr = remove.split(",");
    
    newarray=validatePrompts(removearr);
    emptyList(newarray);
    createList(prompt,newarray);
    }
    
    //Empty child prompts before you insert new filtered values
    function emptyList(arr){
    	for (var jj =0 ; jj < arr.length ; jj++ ){
    		 fW["_oLstChoicesPN_"+arr[jj]].selectedIndex = -1;
    		 fW["_oLstChoicesPN_"+arr[jj]].length = 0;
    	}		
    //------------------to avoid memory leak-----
    	arr=null;
    }
    
    //creates the filter array
    function createList(prompt,newarr) {
    	var len2 = fW["_oLstChoicesPN_"+prompt].length;
    	arrfilter = new Array();
    	var idx = 0;
    	for (var j = 0 ; j < len2; j++){
    		if (fW["_oLstChoicesPN_"+prompt][j].selected == true){
    			arrfilter[idx] = fW["_oLstChoicesPN_"+prompt][j].value;
    			idx++;
    		}
    	}
    	for (var jj =0 ; jj < newarr.length ; jj++ )
    	{
    		createListFilter(prompt,newarr[jj],arrfilter);
    	}
    	
    	//------------------to avoid memory leak-----
    	arrfilter=null;
    	newarr = null;
    }
    
    function createParentFilter(prompt)
    {
    var len2 = fW["_oLstChoicesPN_" + prompt].length;
      	var filter = new Array();
    	var idx = 0;
    
    	for (var j = 0 ; j < len2; j++){
    		if (fW["_oLstChoicesPN_"+prompt][j].selected == true){
    			
    			filter[idx] = fW["_oLstChoicesPN_"+ prompt][j].value;
    			idx++;
    		}
    	}
    	return filter;
    
    }
    function checkParentFilter(string,fltr,flg)
    {
    
    for(var k=0;k<fltr.length;k++)
    					{
    						if(fltr[k]!="All" && flg){
    							if(string.match(fltr[k]))
    							{
    								return true;
    							
    							}
    							
    						}
    						else { return flg;}
    						
    					}if(fltr.length==0) {return true;}
    					flg=false;
    					return flg;
    }					
    
    function createListFilter(filterlevel,filtervalues,filter){
    var parFiltersStr= "ProductLine,ProductType"; 
    	parentarr = new Array();
    	parentarr = parFiltersStr.split(",");
    	newParentArr = validatePrompts(parentarr);
    	parentFilterStr=newParentArr.join();
    	
    ProductLinefilter= new Array();
    	if(parentFilterStr.match("ProductLine")){
    		ProductLinefilter=createParentFilter("ProductLine");
    	}
    	else{
    		ProductLinefilter[0]="All"
    	}
    //-------------------------Product Line filters---
    
    ProductTypefilter = new Array();
    	if(parentFilterStr.match("ProductType")){
    		ProductTypefilter =createParentFilter("ProductType");
    	}
    	else{
    		ProductTypefilter[0]="All"
    	}
    //-------------------------Product Type filters---
    
    var fd;
    var sd;
    var td;
    var frd;
    var tdidx = 0;
    var fdidx = 0;
    var sdidx;
    var frdidx;
    arr = new Array ();
    if (filterlevel == "ProductLine"){
           fd = "!";
    	   sd = "~";				}
    
    	if (filterlevel == "ProductType"){
           fd = "~";
    	   sd = "#";				}
       
       if (filterlevel == "Product"){
           fd = "#";
    	   sd = "^";									
       }
     // ----------------------------------filter Values------------------------------
       
    if (filtervalues == "ProductLine"){
           td = "!";
    	   frd = "~";				
       }
    
    	if (filtervalues == "ProductType"){
           td = "~";
    	   frd = "#";				
       }
        if (filtervalues == "Product"){
           td = "#";
    	   frd = "^";				}   
          
      var len = fW._oLstChoicesPN_ConcatString.length;
    
       for (var idx= 0 ; idx < filter.length ; idx++){
    	for(var i = 0 ; i < len ; i++){
    		
    		var string2 = fW._oLstChoicesPN_ConcatString[i].value;
    		fdidx = string2.indexOf(fd);
    		sdidx = string2.indexOf(sd);
    		tdidx = string2.indexOf(td);
    		frdidx = string2.indexOf(frd);
    		var string3 = string2.substring(fdidx+1,sdidx);
    		if (filter[idx] != "All"){	
    		if ( string3.trim() == filter[idx].trim() ){
    			 var flag=true;
    			 flag=checkParentFilter(string2,ProductLinefilter,flag);
    			 flag=checkParentFilter(string2,ProductTypefilter,flag);
    			if(flag)
    				{
    				
    				arr[i] = string2.substring(tdidx+1,frdidx);
    				}
    		}
    	  }	
    	  else {
    		 	 var flag=true;
    			 flag=checkParentFilter(string2,ProductLinefilter,flag);
    			 flag=checkParentFilter(string2,ProductTypefilter,flag);
    			if(flag)
    				{
    				arr[i] = string2.substring(tdidx+1,frdidx);
    				}
    	 		  }
    	}
      }	
     
     	arr = uniqueVal(arr);
    	filtervalues = "_oLstChoicesPN_"+filtervalues;
    	for (var j=0 ; j < arr.length ; j++){
    	   if (arr[j] != undefined && arr[j] !=null && arr[j] != ""){ 
    		var elOptNew = document.createElement('option');
    		elOptNew.text =  arr[j];
    		elOptNew.value = arr[j];
    		fW[filtervalues].add(elOptNew);
    	   }	
    	}
    	//------------------to avoid memory leak-----
    	arr=null;
    	regionfilter=null;
    	countryfilter=null;
    	filter=null;
    	string2=null;
    }
    function uniqueVal(oldarr){
    oldarr.sort();j=0;
    newarr= new Array()
    for(var i=0;i<oldarr.length;i++){
    newarr[j]=oldarr[i];j++;
    if((i>0)&&(oldarr[i]==oldarr[i-1])){
    newarr.pop();j--
    }
    }
    return newarr;
    }
    </script>

    在代码样例中,该 HTML 项被称为 ‘Cascading Prompts’。这段代码包含了不刷新页面自动实现提示值级联的逻辑。
    Report Studio Prompt 页面:其中在 Product Line、Product Type 和 Product Name 提示下方显示脚本对象
    Report Studio Prompt 页面:其中在 Product Line、Product Type 和 Product Name 提示下方显示脚本对象
  7. 现在转到报表页面的第一页并创建一个显示订单方法和生产成本的柱形图。创建三个对 Product Line、Product Type 和 Product Name 进行过滤的过滤项。例如:
    • [Sales (query)].[Product].[Product line] in ?P_ProductLine?
    • [Sales (query)].[Product].[Product line] in ?P_ProductLine?
    • [Sales (query)].[Product].[Product name] in ?P_Product?
    注意:务必确保过滤表达式中的参数名称与提示页上的参数名称相匹配。
  8. 保存报表。

将自动级联扩展到更多值提示的步骤

  1. 在 ‘Cascading Prompts’ HTML 项代码中,需要为新的提示添加 ‘onchange’ 事件,并需要为该提示传递子提示的名称。
  2. 需要为提示的每个层次修改 parFiltersStr 变量。同样还需要创建新的数组。
  3. 还需要对隐藏提示使用哪个级联功能的查询(当前示例中使用的是 Q_Prompt)进行修改。还需要在查询中添加一列,并加上分隔符。还需要按照查询中定义的分隔符修改/添加 if 条件句中的过滤级和过滤值。
  4. 还需要添加检查父提示值的情形,如代码 “flag=checkParentFilter(string2,ProductLinefilter,flag);” 所示。

对用户生产力的影响

以下是关于本技术如何节省时间的示例。一家公司采用 Cognos 8.4.0 生成 200 多张运营报表,该方法支撑了全球 700 多个活跃用户。通过使用 Cognos 8 开箱即用的功能,‘找不到数据’ 的情况出现得越来越多,由于要多次重新运行报表以获得更多数据,或等待提示重新检索数据显示给用户,从而影响了用户生产力。

通过本文的方法,平均每个用户每张报表节省了一分钟的执行时间。因此,如果每个用户每天运行报表 5 次(700*5)(用户数*执行报表节约的时间),那么每天会提高 3500 分钟生产力。

这只是个示例,具体的结果取决于报表数、返回的数据量和用户数,等等。

参考资料

学习

获得产品和技术

讨论

  • 参与 developerWorks 博客 并加入 developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=811482
ArticleTitle=IBM Cognos 最佳实践: 在 IBM Cognos Report Studio 中实现自动级联提示而不刷新页面
publish-date=04162012