就业培训     下载中心     Wiki     联络
登录   注册

Log
  1. 首页
  2. 学习 Web 开发
  3. Tools and testing
  4. 理解客户端侧 JavaScript 框架
  5. Ember interactivity: Events, classes and state

内容表

  • Adding Interactivity
  • Creating todos
  • Storing Todos with a service
  • Using the service from our header component
  • Displaying our todos
  • 摘要
  • In this module

Ember interactivity: Events, classes and state

  • 上一
  • Overview: Client-side JavaScript frameworks
  • 下一

At this point we'll start adding some interactivity to our app, providing the ability to add and display new todo items. Along the way, we'll look at using events in Ember, creating component classes to contain JavaScript code to control interactive features, and setting up a service to keep track of the data state of our app.

Prerequisites: At minimum, it is recommended that you are familiar with the core HTML , CSS ,和 JavaScript languages, and have knowledge of the terminal/command line .

A deeper understanding of modern JavaScript features (such as classes, modules, etc), will be extremely beneficial, as Ember makes heavy use of them.

Objective: To learn how to create component classes and use events to control interactivity, and keep track of app state using a service.

Adding Interactivity

Now we've got a refactored componentized version of our todo app, lets walk through how we can add the interactivity we need to make the app functional.

When beginning to think about interactivity, it's good to declare what each component's goals and responsibilities are. In the below sections we’ll do this for each component, and then walk you through how the functionality can be implemented.

Creating todos

For our card-header / todo input, we'll want to be able to submit our typed in todo task when we press the Enter key and have it appear in the todos list.

We want to be able to capture the text typed into the input. We do this so that our JavaScript code knows what we typed in, and we can save our todo and pass that text along to the todo list component to display.

We can capture the keydown event via the on modifier , which is just Ember syntactic sugar around addEventListener and removeEventListener (见 this explanation if needed).

Add the new line shown below to your header.hbs 文件:

<input
  class='new-todo'
  aria-label='What needs to be done?'
  placeholder='What needs to be done?'
  autofocus
  {{on 'keydown' this.onKeyDown}}
>

								

This new attribute is inside double curly braces, which tells you it is part of Ember's dynamic templating syntax. The first argument passed to on is the type of event to respond to ( keydown ), and the last argument is the event handler — the code that will run in response to the keydown event firing. As you may expect from dealing with vanilla JavaScript objects , this keyword refers to the "context" or "scope" of the component. One component's this will be different from another component's this .

We can define what is available inside this by generating a component class to go along with your component. This is a vanilla JavaScript class and has no special meaning to Ember, other than extending 从 组件 super-class.

To create a header class to go with your header component, type this in to your terminal:

ember generate component-class header

								

This will create the following empty class file — todomvc/app/components/header.js :

import Component from '@glimmer/component';
export default class HeaderComponent extends Component {
}

								

Inside this file we will implement the event handler code. Update the content to the following:

import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class HeaderComponent extends Component {
  @action
  onKeyDown({ target, key }) {
    let text = target.value.trim();
    let hasValue = Boolean(text);
    if (key === 'Enter' && hasValue) {
      alert(text);
      target.value = ''
    }
  }
}

								

@action decorator is the only Ember-specific code here (aside from extending from the 组件 superclass, and the Ember-specific items we are importing using JavaScript module syntax ) — the rest of the file is vanilla JavaScript, and would work in any application. The @action decorator declares that the function is an "action", meaning it's a type of function that will be invoked from an event that occurred in the template. @action also binds the this of the function to the class instance.

注意: A decorator is basically a wrapper function, which wraps and calls other functions or properties, providing additional functionality along the way. For example, the @tracked decorator (see slightly later on) runs code it is applied to, but additionally tracks it and automatically updates the app when values change. Read JavaScript Decorators: What They Are and When to Use Them for more general information on decorators.

Coming back to our browser tab with the app running, we can type whatever we want, and when we hit Enter we'll be greeted with an alert message telling us exactly what we typed.

the initial placeholder state of the add function, showing the text entered into the input elements being alerted back to you.

With the interactivity of the header input out of the way, we need a place to store todos so that other components can access them.

Storing Todos with a service

Ember has built-in application-level state management that we can use to manage the storage of our todos and allow each of our components to access data from that application-level state. Ember calls these constructs Services , and they live for the entire lifetime of the page (a page refresh will clear them; persisting the data for longer is beyond the scope of this tutorial).

Run this terminal command to generate a service for us to store our todo-list data in:

ember generate service todo-data

								

This should give you a terminal output like so:

installing service
  create app/services/todo-data.js
installing service-test
  create tests/unit/services/todo-data-test.js
								

这创建 todo-data.js file inside the todomvc/app/services directory to contain our service, which initially contains an import statement and an empty class:

import Service from '@ember/service';
export default class TodoDataService extends Service {
}

								

First of all, we want to define what a todo is . We know that we want to track both the text of a todo, and whether or not it is completed.

Add the following import statement below the existing one:

import { tracked } from '@glimmer/tracking';

								

Now add the following class below the previous line you added:

class Todo {
  @tracked text = '';
  @tracked isCompleted = false;
  constructor(text) {
    this.text = text;
  }
}

								

This class represents a todo — it contains a @tracked text property containing the text of the todo, and a @tracked isCompleted property that specifies whether the todo has been completed or not. When instantiated, a Todo object will have an initial text value equal to the text given to it when created (see below), and an isCompleted value of false . The only Ember-specific part of this class is the @tracked decorator — this hooks in to the reactivity system and allows Ember to update what you're seeing in your app automatically if the tracked properties change. More information on tracked can be found here .

Now it's time to add to the body of the service.

First add another import statement below the previous one, to make actions available inside the service:

import { action } from '@ember/object';

								

Update the existing export default class TodoDataService extends Service { … } block as follows:

export default class TodoDataService extends Service {
  @tracked todos = [];
  @action
  add(text) {
    let newTodo = new Todo(text);
    this.todos = [...this.todos, newTodo];
  }
}

								

在这里, todos property on the service will maintain our list of todos contained inside an array, and we'll mark it with @tracked , because when the value of todos is updated we want the UI to update as well.

And just like before, the add() function that will be called from the template gets annotated with the @action decorator to bind it to the class instance. This function's contents are fairly easy to understand — when the function is invoked, a new Todo object instance is created with a text value of text ,和 todos property value is updated to all of the current items inside the array (accessed conveniently using spread syntax ), plus the new todo.

Using the service from our header component

Now that we've defined a way to add todos, we can interact with this service from the header.js input component to actually start adding them.

First of all, the service needs to be injected into the template via the @inject decorator, which we’ll rename to @service for semantic clarity. To do this, add the following import line to header.js , beneath the two existing import lines:

import { inject as service } from '@ember/service';

								

With this import in place, we can now make the todo-data service available inside the HeaderComponent class via the todos object, using the @service decorator. Add the following line just below the opening export... line:

@service('todo-data') todos;

								

Now the placeholder alert(text); line can be replaced with a call to our new add() function. Replace it with the following:

this.todos.add(text);

								

If we try this out in the todo app in our browser ( npm start , go to localhost:4200 ), it will look like nothing happens after hitting the Enter key (although the fact that the app builds without any errors is a good sign). Using the Ember Inspector however, we can see that our todo was added:

The app being shown in the ember inspector, to prove that added todos are being stored by the service, even if they are not being displayed in the UI yet

Displaying our todos

Now that we know that we can create todos, there needs to be a way to swap out our static "Buy Movie Tickets" todos with the todos we're actually creating. In the TodoList component, we'll want to get the todos out of the service, and render a Todo component for each todo.

In order to retrieve the todos from the service, our TodoList component first needs a backing component class to contain this functionality. Press Ctrl + C to stop the development server, and enter the following terminal command:

ember generate component-class todo-list

								

This generates the new component class todomvc/app/components/todo-list.js .

Populate this file with the following code, which exposes the todo-data service, via the todos property, to our template. This makes it accessible via this.todos inside both the class and the template:

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
export default class TodoListComponent extends Component {
  @service('todo-data') todos;
}

								

One issue here is that our service is called todos , but the list of todos is also called todos , so currently we would access the data using this.todos.todos . This is not intuitive, so we'll add a getter 到 todos service called all , which will represent all todos.

To do this, go back to your todo-data.js file and add the following below the @tracked todos = []; line:

get all() {
  return this.todos;
}

								

Now we can access the data using this.todos.all , which is much more intuitive. To put this in action, go to your todo-list.hbs component, and replace the static component calls:

<Todo />
<Todo />

								

With a dynamic #each block (which is basically syntactic sugar over the top of JavaScript's forEach() ) that creates a <Todo /> component for each todo available in the list of todos returned by the service’s all() getter:

{{#each this.todos.all as |todo|}}
  <Todo @todo={{todo}} />
{{/each}}

								

Another way to look at this:

  • this — the rendering context / component instance.
  • todos — a property on this , which we defined in the todo-list.js component using @service('todo-data') todos; . This is a reference to the todo-data service, allowing us to interact with the service instance directly.
  • all — a getter on the todo-data service that returns all the todos.

Try starting the server again and navigating to our app, and you'll find that it works! Well, sort of. Whenever you enter a new Todo item, a new list item appears below the text input, but unfortunately it always says "Buy Movie Tickets".

This is because the text label inside each list item is hardcoded to that text, as seen in todo.hbs :

<label>Buy Movie Tickets</label>

								

Update this line to use the Argument @todo — which will represent the Todo that we passed in to this component when it was invoked in todo-list.hbs , in the line <Todo @todo={{todo}} /> :

<label>{{@todo.text}}</label>

								

OK, try it again. You should find that now the text submitted in the <input> is properly reflected in the UI:

The app being shown in its final state of this article, with entered todo items being shown in the UI

摘要

OK, so that's great progress for now. We can now add todo items to our app, and the state of the data is tracked using our service. Next we'll move on to getting our footer functionality working, including the todo counter, and look at conditional rendering, including correctly styling todos when they've been checked. We'll also wire up our "Clear completed" button.

  • 上一
  • Overview: Client-side JavaScript frameworks
  • 下一

In this module

  • Introduction to client-side frameworks
  • Framework main features
  • React
    • Getting started with React
    • Beginning our React todo list
    • Componentizing our React app
    • React interactivity: Events and state
    • React interactivity: Editing, filtering, conditional rendering
    • Accessibility in React
    • React resources
  • Ember
    • Getting started with Ember
    • Ember app structure and componentization
    • Ember interactivity: Events, classes and state
    • Ember Interactivity: Footer functionality, conditional rendering
    • Routing in Ember
    • Ember resources and troubleshooting
  • Vue
    • Getting started with Vue
    • Creating our first Vue component
    • Rendering a list of Vue components
    • Adding a new todo form: Vue events, methods, and models
    • Styling Vue components with CSS
    • Using Vue computed properties
    • Vue conditional rendering: editing existing todos
    • Focus management with Vue refs
    • Vue resources
  • Svelte
    • Getting started with Svelte
    • Starting our Svelte Todo list app
    • Dynamic behavior in Svelte: working with variables and props
    • Componentizing our Svelte app
    • Advanced Svelte: Reactivity, lifecycle, accessibility
    • Working with Svelte stores
    • TypeScript support in Svelte
    • Deployment and next steps
  • Angular
    • Getting started with Angular
    • Beginning our Angular todo list app
    • Styling our Angular app
    • Creating an item component
    • Filtering our to-do items
    • Building Angular applications and further resources

发现此页面有问题吗?

  • 编辑在 GitHub
  • 源在 GitHub
  • Report a problem with this content on GitHub
  • 想要自己修复问题吗?见 我们的贡献指南 .

最后修改: Jan 22, 2022 , 由 MDN 贡献者

相关话题

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility

版权所有  © 2014-2026 乐数软件    

工业和信息化部: 粤ICP备14079481号-1