[导入]tpl:将tag进行到底

博客主要介绍了tpl的抽象能力和集成能力。在抽象能力方面,tpl编译结果无状态解决性能问题,有完善库机制,借助xml自描述特性可封装概念。在集成能力上,tpl符合xml规范,可使用属性标记,集成EL语言和java数据模型,还能实现强类型化与java接口。
tpl是witrix开发平台中的动态xml标签技术,其基本特点表现为如下三个方面:
1. 执行能力
 xml本身只是对数据的描述,而没有循环和判断的能力。
 在tpl中<c:forEach>和<c:if>标签可以完成程序语言的执行功能,并定义了<c:tile>, <c:iif>等更方便的标签。

2. 抽象能力
   获得抽象能力,首要的一点是使得创建新概念的成本极低。很少有人大量开发jsp tag,原因就是开发tag过于麻烦,而且调整不方便。另外开发出的tag如果粒度太小,大量使用的时候是对性能的致命伤害。
   tpl首先需要经过编译,而不是象jsp tag那样完全动态运行,而且tpl编译出的结果是无状态的,因此解决了性能上的问题。
   在tpl中导入外部模板的语法非常简单,可以实现基本的分解
    <c:include src="xxx.tpl" />
   更重要的是,tpl中定义了完善的库机制。
使用库
    <!-- 导入外部包, 导入时指定名字空间为demo。
         注意tpl中并没有完整的名字空间支持,只能作为qName使用
    -->
    <c:lib src="demo_lib.xml" namespace="demo" />

 <!-- 对具有cache:timeout属性的节点应用cache修饰,即该节点的运行内容被缓存 -->
 <c:decorator type="cache" match="
//@[cache:timeout]" />

    <div id="blockA">
        <p>通过tpl:tag属性可以设定宿主标签的真实标签名。这种做法可以方便可视化设计:</p>
        <input tpl:tag="demo:文本框" value="in block A" otherValue="${varInJsp}" onclick="clickOnBtn()"/>
    </div>

    <div id="blockB">       
       <p>分页表格:</p>
        <img tpl:tag="demo:分页表格" headers="fieldA,fieldB" pager="${pager}" />
    </div>

    <div id="blockC">
      <p>bodyTag标签的调用:</p>
        <demo:循环data>
            <input type="text" value="${row}" /> <br/>
        </demo:循环data>
    </div>

    <div id="blockD" cache:timeout="1000s" >
        <p>条件标签的调用:</p>
        <demo:当Num足够大>
            <p>当 thisObj中的变量 'num' 的值大于3的时候显示这句话 </p>
        </demo:当Num足够大>
    </div>

定义库文件demo_lib.xml
<!--
     自定义标签的属性:
  type: simpleTag, bodyTag 或者 conditionTag, 缺省为simpleTag
  importVars : 每个自定义标签具有自己的变量空间,需要通过importVars来明确指定从外部变量空间中导入的变量。
  demandArgs: 调用时必须明确给出这些参数的值
  otherArgs: 如果指定了该参数,则调用自定义标签时能够使用的参数就必须在demandArgs和otherArgs指定的范围之内。
-->
<demo>
    <!-- 一个简单的tag示例, 直接输出变量。这里demandArgs指定调用时必须提供的变量。 -->
 <文本框 demandArgs="value" otherArgs="otherValue,onclick" type="simpleTag" >
  <p> -----------------------------------  </p>
     <!-- 可以使用调用时提供的其他变量,如otherValue-->
  <input size="40" type="text" value="${value} * ${otherValue}" onclick="${onclick}"/>
  <p> -----------------------------------</p>
 </文本框>

    <!-- 一个自动分页表格,要求传入headers(List类型)指定表头,pager(Pager类型)提供数据 -->
 <分页表格 demandArgs="headers,pager">
     <!-- 从外部导入tpl文件 -->
  <c:include src="flex_table.tpl" />
 </分页表格>

 <!-- 一个bodyTag示例: 循环处理thisObj中的变量data中的每一行。
      importVars从调用环境中自动导入变量而不需要通过调用参数直接指定。
      当然如果调用参数中指定的变量与importVars中指定的变量重复,则调用参数会覆盖importVars.
    -->
 <循环data importVars="thisObj" type="bodyTag">
  <c:forEach var="row" items="${data}">
   <!-- tagBody为调用时标签的内容 -->
   <cp:compile src="${tagBody}" />
  </c:forEach>
 </循环data>

 <!-- 一个条件标签的示例.
      条件标签一般不应该输出文本,而是返回一个bool值。仅当返回true的时候,调用时标签的内容才会被运行。
 -->
 <当Num足够大 importVars="thisObj" type="conditionTag">
  <l:gt name="num" value="3" />
 </当Num足够大>
</demo>

注 意到bodyTag和conditionTag使得我们可以将循环(容器)逻辑和判断逻辑抽象成有意义的概念,而不再是一些执行的指令。这种抽象能力的应 用非常依赖于xml的自描述特性,我们不需要付出太大的努力,就可以在lib中封装出一个有意义的概念来! 而且tag的参数可以指定,可以缺省,存在多种选择,也对应着多种逻辑模型。
通过<c:decorator>的用法可以看到,因为xml明确定义了结构,使得我们可以轻易的实施类似AOP的功能。

3. 集成能力
   首先,tpl完全符合xml规范,与xml世界接轨,可以自由的使用xslt。例如,在
   任何标签中都可以指定tpl:xdecorator属性, tpl在编译之前首先会应用指定的xslt文件进行变换,对变换后的结果再进行编译。
   <anyTagName tpl:xdecorator="some_xslt.xslt">
  ...
   </anyTagName>
   其次,tpl可以使用属性标记,即tpl标签不仅仅可以通过标签名来标识,而且可以通过tpl:tag属性来标识。例如:
   <input tpl:decorator="demo:文本框" />
   与 <demo:文本框 />等效。
   这种做法避免了tpl侵入html模型,使得我们可以利用现有工具对tpl进行所见即所得(WISIWIG)的设计。

   再次, 在tpl中使用EL(Expression Language)语言,集成java数据模型。

   第四, 在tpl中可以采用如下方式实现强类型化,完成与java的接口。
   interface ITest{
     void test(String name, int value);
   }

   <o:class name="MyClass" implements="ITest">
    <test args="name,value" >
   ...
    </test>
   </o:class>
   这种能力可以在数据源的filter中使用。

   第五, tpl可以轻易的集成其它xml技术。例如,tpl集成了ant
   <ant:run>
  <echo message="xxx" />
   </ant:run>
   因此,我们立刻拥有了ant的数千个功能标签。
import React, { useState } from 'react'; import { View, Text, TouchableOpacity, Image, Alert, StyleSheet, Platform, NativeModules } from 'react-native'; // 导入鸿蒙权限模块(解决权限相关爆红) const { Permissions } = NativeModules; // 声明鸿蒙环境下的模块类型(解决类型缺失爆红) declare module '@react-native-oh-tpl/react-native-image-picker' { export interface LaunchOptions { title?: string; storageOptions?: { skipBackup?: boolean; path?: string; }; mediaType?: 'photo' | 'video' | 'mixed'; } export interface ImagePickerResponse { didCancel?: boolean; error?: string; uri?: string; type?: string; fileName?: string; fileSize?: number; width?: number; height?: number; } export function launchImageLibrary(options: LaunchOptions, callback: (response: ImagePickerResponse) => void): void; export function launchCamera(options: LaunchOptions, callback: (response: ImagePickerResponse) => void): void; } declare module '@react-native-ohos/react-native-fs' { export function exists(path: string): Promise<boolean>; export function readFile(path: string, encoding: 'base64' | 'utf8'): Promise<string>; export const MainBundlePath: string; export const CachesDirectoryPath: string; export const DocumentDirectoryPath: string; } declare module '@react-native-oh-tpl/camera-roll' { export function saveToCameraRoll(tag: string, type?: 'photo' | 'video'): Promise<string>; } import ImagePicker, { ImagePickerResponse, LaunchOptions, } from '@react-native-oh-tpl/react-native-image-picker'; import RNFS from '@react-native-ohos/react-native-fs'; import CameraRoll from '@react-native-oh-tpl/camera-roll'; // 定义图片源类型 type ImageSource = { uri: string } | null; const ImageHandler: React.FC = () => { const [selectedImage, setSelectedImage] = useState<ImageSource>(null); const [isSaving, setIsSaving] = useState<boolean>(false); /** * 检查并请求必要的权限 * @returns 是否获得所有必要权限 */ const checkPermissions = async (): Promise<boolean> => { if (Platform.OS === 'harmony') { // 鸿蒙系统权限处理(完善权限请求逻辑) try { if (Permissions) { const permissions = [ 'ohos.permission.READ_IMAGEVIDEO', 'ohos.permission.WRITE_IMAGEVIDEO' ]; // 调用鸿蒙原生权限请求方法 const result = await Permissions.requestPermissions(permissions); return permissions.every(perm => result[perm] === 'granted'); } return true; } catch (err) { console.error('鸿蒙权限请求失败:', err); return false; } } else if (Platform.OS === 'android') { // Android平台权限处理 return new Promise(resolve => resolve(true)); } // iOS平台 return true; }; /** * 从图库选择图片 */ const pickImage = async () => { const hasPermission = await checkPermissions(); if (!hasPermission) { Alert.alert('权限不足', '请授予图片访问权限'); return; } const options: LaunchOptions = { title: '选择图片', storageOptions: { skipBackup: true, path: 'images', }, mediaType: 'photo', }; ImagePicker.launchImageLibrary(options, (response: ImagePickerResponse) => { console.log('ImagePicker响应:', response); if (response.didCancel) { console.log('用户取消了选择'); } else if (response.error) { console.log('ImagePicker错误:', response.error); Alert.alert('错误', `无法获取图片: ${response.error}`); } else if (response.uri) { setSelectedImage({ uri: response.uri }); } }); }; /** * 拍摄照片 */ const takePhoto = async () => { const hasPermission = await checkPermissions(); if (!hasPermission) { Alert.alert('权限不足', '请授予相机和存储权限'); return; } const options: LaunchOptions = { title: '拍摄照片', storageOptions: { skipBackup: true, path: 'images', }, mediaType: 'photo', }; ImagePicker.launchCamera(options, (response: ImagePickerResponse) => { console.log('相机响应:', response); if (response.didCancel) { console.log('用户取消了拍摄'); } else if (response.error) { console.log('相机错误:', response.error); Alert.alert('错误', `无法访问相机: ${response.error}`); } else if (response.uri) { setSelectedImage({ uri: response.uri }); } }); }; /** * 保存图片到相册 */ const saveImageToGallery = async () => { if (!selectedImage) { Alert.alert('提示', '请先选择一张图片'); return; } setIsSaving(true); try { const hasPermission = await checkPermissions(); if (!hasPermission) { Alert.alert('权限不足', '请授予保存图片权限'); return; } // 处理文件路径 let filePath = selectedImage.uri; if (filePath.startsWith('file://')) { filePath = filePath.replace('file://', ''); } // 验证文件是否存在 const fileExists = await RNFS.exists(filePath); if (!fileExists) { throw new Error(`文件不存在: ${filePath}`); } // 读取文件内容(base64格式) const fileData = await RNFS.readFile(filePath, 'base64'); // 保存到相册 const result = await CameraRoll.saveToCameraRoll( `data:image/jpeg;base64,${fileData}`, 'photo' ); console.log('图片保存成功:', result); Alert.alert('成功', '图片已保存到图库'); } catch (error) { console.error('保存图片失败:', error); Alert.alert('失败', `无法保存图片: ${error instanceof Error ? error.message : String(error)}`); } finally { setIsSaving(false); } }; return ( <View style={styles.container}> <Text style={styles.title}>图片选择与保存</Text> <View style={styles.buttonsContainer}> <TouchableOpacity style={styles.button} onPress={pickImage}> <Text style={styles.buttonText}>从图库选择</Text> </TouchableOpacity> <TouchableOpacity style={styles.button} onPress={takePhoto}> <Text style={styles.buttonText}>拍摄照片</Text> </TouchableOpacity> </View> {selectedImage && ( <View style={styles.imageContainer}> <Image source={selectedImage} style={styles.image} resizeMode="contain" accessibilityLabel="选中的图片预览" /> <TouchableOpacity style={[styles.saveButton, isSaving && styles.savingButton]} onPress={saveImageToGallery} disabled={isSaving} > <Text style={styles.saveButtonText}> {isSaving ? '保存中...' : '保存到图库'} </Text> </TouchableOpacity> </View> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, backgroundColor: '#f5f5f5', }, title: { fontSize: 22, fontWeight: 'bold', marginBottom: 30, textAlign: 'center', color: '#333', }, buttonsContainer: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: 30, gap: 10, }, button: { backgroundColor: '#1677ff', padding: 12, borderRadius: 8, flex: 1, alignItems: 'center', maxWidth: 150, }, buttonText: { color: 'white', fontSize: 16, fontWeight: '500', }, imageContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', }, image: { width: '100%', height: '70%', borderRadius: 8, marginBottom: 20, borderWidth: 1, borderColor: '#ddd', backgroundColor: '#fff', }, saveButton: { backgroundColor: '#36d399', padding: 12, borderRadius: 8, width: '80%', alignItems: 'center', }, savingButton: { backgroundColor: '#9ae6b4', }, saveButtonText: { color: 'white', fontSize: 16, fontWeight: 'bold', }, }); export default ImageHandler; 帮我修改代码
最新发布
08-16
``` <view style="height:200rpx"></view> <view wx:if="{{doHome}}" bindtap="bindHomeTap" class="cmpt-fixed-home-btn"><text class="icon-home"></text><text style="font-size:22rpx;">首页</text></view> <!--回页首 begin--> <block wx:if="{{doTop}}"> <import src="../../../tpls/public/top_tpl.wxml" /> <template is="topTpl" data="{{topBtnShow, bottom:topBtnBottom}}" /> </block> <!--回页首 end--> <!--mode1 BEGIN--> <view wx:if="{{mode=='mode1'}}" class="cmpt-biz-detail-mode1 safe-bottom"> <view wx:if="{{tag}}" class="has-tag">{{tag}}</view> <view class="fav btn-inner" bindtap="bindFavTap" wx:if="{{doFav}}"> <block wx:if="{{isFav>0}}"> <text class="icon-favorfill margin-right-xxs text-project" style="color:{{bg}}!important"></text><text class="text-project" style="color:{{bg}}!important">已收藏</text> </block> <block wx:else> <text class="icon-favor margin-right-xxs"></text>加入收藏 </block> </view> <view class="btn-inner" bindtap="bindShareTap" wx:if="{{doShare&&doPoster}}"> <view class="share"><text class="icon-forward margin-right-xxs"></text>分享</view> </view> <view class="btn-inner" wx:if="{{doShare&&!doPoster}}"> <button class="share clearbtn" open-type="share" style="margin-top:-10rpx"><text class="icon-forward margin-right-xxs"></text>分享</button> </view> <slot /> </view> <!--mode1 end--> <!--mode2 BEGIN--> <view wx:if="{{mode=='mode2'}}" class="cmpt-biz-detail-mode2 safe-bottom"> <view wx:if="{{tag}}" class="has-tag">{{tag}}</view> <view class="inner"> <button class="share clearbtn" bindtap="bindShareTap" wx:if="{{doShare&&doPoster}}"> <text class="icon-forward"></text> <text class="text-s">分享</text> </button> <button class="share clearbtn" open-type="share" wx:if="{{doShare&&!doPoster}}"> <text class="icon-forward"></text> <text class="text-s">分享</text> </button> <block wx:if="{{doFav}}"> <view bindtap="bindFavTap" class="fav text-project" wx:if="{{isFav>0}}"> <text class="icon-favorfill"></text> <text class="text-s">已收藏</text> </view> <view bindtap="bindFavTap" class="fav" wx:else> <text class="icon-favor"></text> <text class="text-s">收藏</text> </view> </block> <view class="slot-inner"> <slot /> </view> </view> </view> <!--mode2 end--> <cmpt-poster model:show="{{showPoster}}" doPoster="{{doPoster}}" wx:if="{{ posterConfig}}" config="{{posterConfig}}" />```分析代码作用
03-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值