一个模块的template模板、JavaScript和css之间的关系其实可以如下图表示:

如果你了解Angular、Vue动态模板,那你将会对Amaple的模板感到很熟悉,在Amaple中,template模板也是基于模板指令和状态数据的动态模板引擎,当一个状态数据改变时,在template模板中与它绑定的dom元素将自动作出相应的更新,所以此时你只需关心模块内的状态数据,而不需去理会视图层的更新。

指令类型

指令分为动态指令和静态指令,动态指令的值会被当作JavaScript代码被解析,所以它们可以获取并绑定状态属性的值,如:if:for等指令;而静态属性的值只会被当做普通的字符串处理,无法绑定状态属性,如:module:ref指令。

使用插值表达式输出文本

我们直接先看看在index.html模板中使用插值表达式输出文本和属性,你应该会很快明白是怎么回事了:

<module>
<template>
<!-- 插值表达式由{{ }}来表示 -->
<!-- 插值表达式可在dom元素内和元素属性上进行状态数据的绑定 -->
<span>{{ text }}</span> <!-- 插值表达式也可以多个插值表达式同时使用,或与固定字符串混合使用 -->
<a href="{{ link }}?{{ search }}">direct to page about</a>
</template> <script> // init函数将返回状态属性对象用于解析挂载模板数据
new am.Module ( {
init : function () {
return {
text : "hello amaplejs,page index",
link : "/about",
search : "from=index"
};
},
mounted : function () { // 通过this.state获取所有的状态属性
this.state.text = "text has changed";
// this.state.text被赋值后模板中“<span>hello amaplejs,page index</span>”也将自动更新为“<span>text has changed</span>”
// 在queryUpdated、paramUpdated和unmount周期函数中也可以通过此方法来获取或赋值状态属性的值
}
} );
</script> <style scoped>
span { font-size: 20px; }
</style>
</module>

【注意】①. <template>模板中的取值范围为当前模块的状态数据对象,在上面示例中,解析挂载时{{ text }}被替换为状态数据的text属性值;
②. 插值表达式的{{}}将被作为JavaScript代码解析,如你可以这样写{{ text === 1 ? "show" : "hidden" }},表示text属性值等于数字1时输出"show",否则输出"hidden"

# 插值表达式在style与class属性的特殊表现

插值表达式一般输出字符串(状态属性值不为字符串时将会调用该值的toString函数),但在style属性上使用插值表达式时会将一个object对象转换为内联样式的格式,在class属性上使用时会将一个array数组转换为以空格隔开的字符串:

<template>
<div class="{{ clsList }}" style="{{ styleObj }}"><div>
</template>
<script>
new am.Module ( {
init : function () {
return { // 在class属性使用插值表达式输出时自动转换为"cls1 cls2"
clsList: [ "cls1", "cls2" ], // 在style属性使用插值表达式输出时自动转换为"background:red;font-size:20px"
// 注意两点:
// ①.使用驼峰式来定义css属性名,如fontSize,在输出时将自动转换为font-size
// ②.为css属性名赋值为一个量值时可直接写为数字,如fontSize: 20,它将自动补充“px”单位
styleObj: {
background: "red",
fontSize: 20
}
};
},
} );
</script>

循环渲染输出dom元素

在实际项目中,经常会遇到根据数据库的数据来渲染一个列表的需求,如用户列表、使用表格展示数据等,此时我们就需要使用模板指令:for来完成需求:

<template>
<ul>
<!-- :for指令中只能使用两种特定语法 -->
<!-- 第一种为“item in list”,item为list循环时的每一项值,它是一个局部变量,只能在:for属性所在的元素上及内部使用 -->
<!-- 有时候我们需要获取遍历时的索引,此时你可以这样写“(item, i) in list”,这样就可以在:for属性所在的元素上及内部使用“i”变量了 -->
<li :for="item in list">{{ item }}</li>
</ul>
</template> <script>
new am.Module ( {
init : function () {
return {
list: [ "apple", "orange", "grape" ]
};
},
mounted : function () { // 我们可以调用数组的相关变异函数,在模板中绑定了此数组的地方也将作出相应更新,如下
this.state.list.push ( "peach" );
// 其他可用的变异函数还有push、pop、sort、shift、unshift、splice、reverse
}
} );
</script>

# 在<template>上使用:for

你可以在<template>上使用:for来循环渲染多个dom元素:

<template>
<div>
<template :for="i in names">
<span>list.firstName</span>
<span>list.lastName</span>
</template>
</div>
</template> <script>
new am.Module ( {
init : function () {
return {
names: [
{firstName: "George", lastName: "Bush"},
{firstName: "Jake", lastName: "Wood"}
]
};
},
} );
</script>

它将被渲染为:

<div>
<!-- <template>标签自动被去除 -->
<span>George</span>
<span>Bush</span>
<span>Jake</span>
<span>Wood</span>
</div>

# 使用:for指令遍历字符串

:for指令遍历字符串时,item值为字符串每个字符:

<div>
<strong>Amaple由</strong>
<span :for="char in 'Amaple'">[{{ char }}]</span>
<strong>组成</strong>
</div>
<!-- 渲染后的文字为:Amaple由[A][m][a][p][l][e]组成 -->

# 使用:for指令遍历数字

:for指令遍历数字时,item值为从0开始累加的索引数字:

<div>
<strong>小于5的非负数有</strong>
<span :for="num in 5">[{{ num }}]</span>
</div>
<!-- 渲染后的文字为:小于5的非负数有[0][1][2][3][4] -->

【注意】使用状态数组应该避免直接通过索引操作,如this.state.list [ 0 ] = "banana"将不会触发自动更新。

通过条件判断显示与隐藏元素

我们经常需要通过条件判断来确定应该显示哪一部分的内容,以简单的用户登录为例,当有用户信息时显示信息,没有时显示登录按钮,此时可以使用模板指令的:if:else-if:else,它与我们熟知的ifelse ifelse关键字的用法相同:

<template>
<div :if="userInfo">
<img src="{{ userInfo.avatar }}" />
<span>{{ userInfo.username }}</span>
</div>
<div :else>
<a href="login.action">您还没有登录,点击登录</a>
</div>
</template>
<script>
new am.Module ( {
init : function () {
return {
userInfo : null
};
},
mounted : function () {
this.state.userInfo = { username: "Tom", avatar: "tom_101101.jpg" };
}
} );
</script>

当初始化时module.state.userInfo=null,它将被渲染为:

<div>
<a href="login.action">您还没有登录,点击登录</a>
</div>

mounted钩子函数触发后module.state.userInfo={ username: "Tom", avatar: "tom_101101.jpg" },它将被渲染为:

<div>
<img src="tom_101101.jpg" />
<span>Tom</span>
</div>

# 在<template>上使用条件判断

它将<template>的子元素作为一个整体进行条件判断,并在渲染的时候去掉<template>父元素。

<template>
<div>
<template :if="show === true">
<span>content1</span>
<span>content2</span>
</template>
<template :else-if="show === false">
<span>content3</span>
<span>content4</span>
</template>
<div>
<span>content5</span>
</div>
</div>
</template> <script>
new am.Module ( {
init : function () {
return {
show: true
};
},
} );
</script>

module.state.show=true时将被渲染为:

<div>
<span>content1</span>
<span>content2</span>
</div>

当module.state.show=false时将被渲染为:

<div>
<span>content3</span>
<span>content4</span>
</div>

当module.state.show=1时将被渲染为:

<div>
<div>
<span>content5</span>
</div>
</div>

# :for:if在同一个元素上使用

:for指令的解析优先级高于:if,这意味着所有循环渲染的元素都会判断:if的条件。

<template>
<ul>
<li :for="item in list" :if="item !== 'apple'">{{ item }}</li>
</ul>
</template> <script>
new am.Module ( {
init : function () {
return {
list: [ "apple", "orange", "grape" ]
};
},
} );
</script>

它将被渲染为:

<ul>
<li>orange</li>
<li>grape</li>
</ul>

【注意】带有:else-if:else属性元素的上一个兄弟元素必须使用了:if:else-if指令,且:else属性是没有值的

模块函数与事件绑定

在状态数据对象上定义的函数叫做模块函数,我们在子模块主动向父模块传值时提到过它。除了传值的作用外,模块函数还可当做事件绑定函数来使用,如下面事件绑定的示例,在<button>上使用:on指令声明click事件并指定回调函数。

<template>

    <!-- :on指令的用法是在它的后面补充上事件名称,如:onclick、:onchange等 -->
<!-- 使用这个指令绑定事件共有三种形式 -->
<!-- ①.直接指定一个模块函数作为事件回调函数 -->
<button :onclick="clickHandler">BTN1</button> <!-- ②.在指定一个模块函数为事件回调函数的同时传递参数到此模块函数内 -->
<button :onclick="clickHandler ( 'a', 111 )">BTN2</button> <!-- ③.当你认为你的事件处理函数很简单,并且不会在其他地方重用时,那你也可以直接将JavaScript代码写到:on指令的值中 -->
<button :onclick="var text = 'BTN2 has clicked!'; alert(text);">BTN3</button>
</template>
<script>
new am.Module ( {
init : function () {
return { // 需注意的是,作为事件绑定的模块函数的第一个参数永远为event对象,而在额外传入的参数在模块函数中第二个开始接收
// 在第②种形式中传入了字符串"a"和数字111到模块函数中,模块函数在第二个开始接收,即str的值为"a",num为111
clickHandler: function ( event, str, num ) { ... }
};
}
} );
</script>

【注意】模块函数内的this指针指向当前模块的状态数据对象(即module.state对象),它也不可使用ES6的箭头函数(Arrow function),因为这样会导致函数内this指针指向不正确而出错。

计算属性

假如项目的某个模块中定义了两个状态属性,分别为产品品牌brand、产品型号model,此时你需要在模板中输出品牌与型号的组合,此时你可能会这样写:

<template>
<span>{{ brand }} {{ model }}</span>
</template>
<script>
new am.Module ( {
init : function () {
return {
brand : "Samsung",
model : "Note8"
};
}
} );
</script>

这没有任何问题,但当有多处都需输出同样信息时,使用多个差值表达式就显得有点麻烦,所以对于如需要多次拼接字符串或其他任何较为复杂处理的输出,应该使用计算属性来实现,像这样:

<template>
<span>{{ productName }}</span>
</template>
<script>
new am.Module ( {
init : function () {
return {
brand: "Samsung",
model: "Note8", // 在状态数据对象中指定computed属性,它的值为一个对象,此对象内的所有属性都将被转换为计算属性
computed: { // 这里定义了一个计算属性productName,它分别有get和set函数
// 框架在初始化计算属性时将会调用get函数获取并缓存此计算属性的值,而获取this.state.productName的值时都将会返回缓存的值
// 当为this.state.productName赋值时,则会调用此计算属性的set函数并接收一个新值
// 从该计算属性productName的get函数可看出,它依赖于状态数据brand和model,所以当brand或model的值更新时,此计算属性的缓存值也会更新
productName: {
get: function () {
return this.brand + " " + this.model;
},
set: function ( val ) {
var split = val.split ( " " );
this.brand = split [ 0 ];
this.model = split [ 1 ];
}
} // 一个计算属性可以只设置get函数,你可以像上面那样直接去掉set函数,也可以直接将此计算属性定义为一个函数作为get函数,像这样
// productName: function () { return this.brand + " " + this.model; }
}
};
}
} );
</script>

表单的双向绑定

在表单元素上使用:model指令即可实现表单输入值与状态属性的双向绑定,如下:

<template>
<!-- 当此input输入框的value值改变时,状态属性username的值也将自动同步更新 -->
<input type="text" name="username" :model="username" />
</template>
<script>
new am.Module ( {
init : function () {
return { // 当此状态属性的值改变时,上面的input输入框的value值也将同步更新
username: ""
};
}
} );
</script>

可使用model指令的表单元素有type为任何值的inputtextareaselect

# :model指令在checkbox上使用

checkbox绑定的状态属性值必须为Array类型,且当多个checkbox绑定同一个状态属性时,被选中checkbox的值将会保存在此数组中。

# :model指令在radio上使用

radio元素没有设置name属性时,自动设置绑定的状态属性名为name属性。

以上所有指令为动态指令,接下来的是静态指令

设置当前页面显示的标题

当url跳转更新模块时,我们希望标题随模块改变,此时我们可使用:title指令来设置标题,它只能用于一个模块的<module>元素上,像这样:

<module :title="hello amaplejs">
<template>...</template>
<script>...</script>
<style>...</style>
</module>

当然很多时候一个页面将同时加载多个不同层级的模块,此时框架将会从上到下、从外到内的顺序解析并更新模块,当以这样的顺序解析时将会获取第一个:title不为空的标题作为更新标题,而会自动忽略后面模块所定义的标题,所以建议标题应该在最外层的主模块中定义。

引用元素

有时候必须操作dom元素如聚焦input元素,我们必须获取此input元素的对象并调用input.focus函数,这时可以在此input元素上使用:ref指令定义一个引用,然后调用am.Module对象的成员函数refs( refName )获取被引用的dom元素:

<template>
<input type="text" :ref="ref_dom" />
</template>
<script>
new am.Module ( {
init : function () { ... },
mounted : function () { // 这样就获取到了模板中的input元素并聚焦
this.refs ( "ref_dom" ).focus ();
// refs函数不能在init生命周期函数中使用,因为init函数被调用时还未解析模板
}
} );
</script>

当多个元素的:ref值设置同一个引用名时,使用refs函数获取该引用名的dom元素时将会返回一个包含所有该引用名的元素数组。

继续学习下一节:【AmapleJS教程】4. 组件
也可回顾上一节:【AmapleJS教程】2. 模块

【Amaple教程】3. 模板指令与状态数据(state)的更多相关文章

  1. 【Amaple教程】4. 组件

    在Amaple单页应用中,一个页面其实存在两种模块化单位,分别是 模块 (am.Module类),它是以web单页应用跳转更新为最小单位所拆分的独立块: 组件 (am.Component类),它的定位 ...

  2. 【Amaple教程】2. 模块

    正如它的名字,模块用于amaplejs单页应用的页面分割,所有的跳转更新和代码编写都是以模块为单位的. 定义一个模块 一个模块由<module>标签对包含,内部分为template模板.J ...

  3. SpringBoot图文教程10—模板导出|百万数据Excel导出|图片导出「easypoi」

    有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1「概念+ ...

  4. pageadmin CMS网站制作教程:模板中的站点数据调用

    pageadmin CMS网站建设教程:模板中的站点数据调用 1.获取当前站点Id,返回int数字 Html.CurrentSiteId() 2.获取当前站点url地址,返回string字符串 Htm ...

  5. Handlebars的基本用法 Handlebars.js使用介绍 http://handlebarsjs.com/ Handlebars.js 模板引擎 javascript/jquery模板引擎——Handlebars初体验 handlebars.js 入门(1) 作为一名前端的你,必须掌握的模板引擎:Handlebars 前端数据模板handlebars与jquery整

    Handlebars的基本用法 使用Handlebars,你可以轻松创建语义化模板,Mustache模板和Handlebars是兼容的,所以你可以将Mustache导入Handlebars以使用 Ha ...

  6. AngularJS1.X学习笔记3-内置模板指令

    前面学习了数据绑定指令,现在开始学习内置模板指令.看起来有点多,目测比较好理解.OK!开始! 一.ng-repeat 1.基本用法 <!DOCTYPE html> <html lan ...

  7. ASP.NET Core教程【二】从保存数据看特有属性与服务端验证

    前文索引: 在layout.cshtml文件中,我们可以看到如下代码: <a asp-page="/Index" class="navbar-brand" ...

  8. ASP.NET Core教程【二】从保存数据看Razor Page的特有属性与服务端验证

    前文索引:ASP.NET Core教程[一]关于Razor Page的知识 在layout.cshtml文件中,我们可以看到如下代码: <a asp-page="/Index" ...

  9. vue 基础重要组件 模板指令 事件绑定

    组件:data methods watch new vue({ data:{ a:1, b:[] }, methods:{ dosomething:function(){ this.a++; } }, ...

随机推荐

  1. 利用 Monitor.TryEnter 来规避 .NET 线程死锁的源代码

    在开发多线程的应用程序时,我们会大量用到 lock (...) {} 块.如果 lock 的对象比较多,非常容易发生死锁.死锁的发生很难预料,而且一旦发生在界面线程上,界面就不再刷新响和应用户输入:如 ...

  2. 查询表中列转换为json

    DECLARE @sql VARCHAR(MAX) SET @sql= (SELECT (select '+'',"'+column_name+'":"''+CAST(' ...

  3. GNU编译器:Codesourcery

    Codesourcery G++是个商业软件, 不过它有个lite版本,是完全免费的,只不过没有IDE,只有commmand line. Codesourcery G++支持coldfire, pow ...

  4. hadoop--大数据生态圈中最基础、最重要的组件

    hadoop是什么? hadoop是一个由Apache基金会所开发的分布式系统基础架构,hdfs分布式文件存储.MapReduce并行计算.主要是用来解决海量数据的存储和海量数据的分析计算问题,这是狭 ...

  5. markdown实现点击链接下载文件

    今天用Markdown工具,需要实现一个点连接下载文件的功能,看起来很多简单我也没多想就直接写了,并且单个页面测试的时候也挺正常,就发布了,但是发布后使用的时候发现问题了,浏览器中直接点击链接没反应, ...

  6. Go语言基础之操作Redis

    Go语言操作Redis 在项目开发中redis的使用也比较频繁,本文介绍了Go语言如何操作Redis. Redis介绍 Redis是一个开源的内存数据库,Redis提供了5种不同类型的数据结构,很多业 ...

  7. Linux根文件系统和目录结构及bash特性4

    文件管理工具:cp,mv,rm    cp命令:copy                cp命令主要用于复制文件或目录 语法:        单源复制        cp [OPTION]... [- ...

  8. oracle学习1 基于oracle数据库的PLSQL编程以及存储过程的创建和使用视频

    https://www.bilibili.com/video/av46777605 plsql中选择testWindow中可以进行测试 1.编写函数在plsql的testwindow中 begin d ...

  9. DP tricks and experiences

    [LeetCode] 关于动态规划的经验与技巧. 很多时候多分配一位空间是为了递推的方便,例如前推两位. 辅助数组的索引,用到的可能是 1 — N,或者是 0 — N-1. 具体要看清 f[i] 每一 ...

  10. JAVA 流与文件

    流 InputStream和OutputStream是所有的输入流和输出流的超类.他们两个都是抽象类. read方法和write方法都是阻塞方法,这意味着如果不能里可以写入或者读取,比如因为网络问题, ...