Source: https://github.com/enepomnyaschih/mt/tree/mt-2.1-2 (Git branch).
In this part we meet List class. We will learn how to use it to display child UI component lists. Our goal is to render a list of tweets developed in the previous part.
Let's dive directly to the view. Define class TweetFeed.
<div jwclass="mt-tweet-feed"> <div jwid="header">Tweets</div> <div jwid="tweets"></div> <div jwid="footer">...</div> </div>
import Component from "jwidget/Component"; import List from "jwidget/List"; import template from "jwidget/template"; import Tweet from "../model/Tweet"; import TweetView from "./TweetView"; @template(require<string>("./TweetFeed.jw.html")) export default class TweetFeed extends Component { constructor(private tweets: Tweet[]) { super(); } protected renderTweets() { const tweetViews = this.tweets.map(tweet => new TweetView(tweet)); return this.own(new List(tweetViews)).ownItems(); } }
Let's review renderTweets method in details. Similarly to TweetView component, we've defined method render<ChildId> for element with jwid="tweets". But now this method not just fills the element with data, but renders a list of child components into it. This list is created from an array of tweet models with the following steps:
jWidget components recognize jWidget lists of components, not arrays. If you return such a list as a result of render<ChildId> method, then this list gets rendered into the corresponding element as a list of child components.
Aggregation methods own and ownItems control the life time of child components. If object A owns object B, then destruction of object A automatically triggers destruction of object B. In our case, destruction of TweetFeed automatically triggers destruction of tweet view list. Thanks to ownItems call, destruction of the list triggers destruction all its elements, i.e. tweet views. It is a good practice to destroy child UI components when you don't need them anymore, because any UI component may initialize its own bindings you can be unaware of. See Common practices in child component management for more instructions about how this can be achieved.
Let's define styles.
.mt-tweet-feed background #fff border 1px solid rgba(0,0,0,0.45) border-radius 6px box-sizing border-box width 522px &-header color #333 font-family Arial, sans-serif font-size 18px font-weight bold padding 10px text-shadow 0 1px 0 #fff &-footer border-top 1px solid #e8e8e8 padding 8px text-align center
Add the file to index.styl:
// All Stylus files should be imported here in the preferred order @import "view/TweetFeed" @import "view/TweetView"
And prepare new test data.
import "es6-promise/auto"; import "script-loader!jquery"; import "./index.styl"; import {createTweetByJson} from "./model/Tweet"; import TweetFeed from "./view/TweetFeed"; $(function () { const tweets = [ { "fullName": "Road Runner", "shortName": "roadrunner", "avatarUrl48": "backend/avatar-48.png", "contentHtml": "jWidget documentation is here <a href=\"https://enepomnyaschih.github.com/jwidget\" target=\"_blank\">enepomnyaschih.github.com/jwidget</a>", "timeAgo": 215000, "like": false, "retweet": true }, { "fullName": "Road Runner", "shortName": "roadrunner", "avatarUrl48": "backend/avatar-48.png", "contentHtml": "Tweet feed is growing", "timeAgo": 515000, "like": false, "retweet": false } ].map(createTweetByJson); new TweetFeed(tweets).renderTo("body"); });
Running the application in the browser displays the expected result.
Let's review one more way of child component rendering, without render<ChildId> method definition. Let's remove renderTweets method and override afterRender method instead:
import Component from "jwidget/Component"; import List from "jwidget/List"; import template from "jwidget/template"; import Tweet from "../model/Tweet"; import TweetView from "./TweetView"; @template(require<string>("./TweetFeed.jw.html")) export default class TweetFeed extends Component { constructor(private tweets: Tweet[]) { super(); } protected afterRender() { super.afterRender(); const tweetViews = this.tweets.map(tweet => new TweetView(tweet)); const tweetViewList = this.own(new List(tweetViews)).ownItems(); this.addList(tweetViewList, "tweets"); } }
This code is equivalent to the original one, but child component list is added dynamically with addList method. This method takes element "jwid" as second argument, which should be used as a container for child components passed in the first argument. If we won't pass second argument, the array will be rendered into root element. Use the way you like more. I'll stick to the first way, utilizing render<ChildId> method.
Tutorial. Part 3. Named child components