第一个 Angular 项目 - 动态页面

这篇具有很好参考价值的文章主要介绍了第一个 Angular 项目 - 动态页面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第一个 Angular 项目 - 动态页面

使用的所有技巧都在下面的笔记里:

  • [Angular 基础] - 数据绑定(databinding)

  • [Angular 基础] - 指令(directives)

    以上为静态页面,即不涉及到跨组件交流的内容

    以下涉及到组件内的沟通,从这开始数据就“活”了

  • [Angular 基础] - 自定义事件 & 自定义属性

  • [Angular 基础] - 视图封装 & 局部引用 & 父子组件中内容传递

  • [Angular 基础] - 生命周期函数


静态页面的实现在这里:第一个 Angular 项目 - 静态页面

这篇笔记结合新学的绑定知识,盘活数据

拟态路由

Angular 有对 routing 的 built-in 支持,鉴于这里暂时还没有学到 routing,所以这里用的是拟态的方式去实现。这里的需求是 3 个点:

  1. 导航栏有一个对所处的页面进行高亮

  2. 切换到 recipe 页面只显示 recipe 的内容

  3. 切换到 shopping-list 页面只显示 shopping list 相关的内容

接下来对功能的实现进行分析:

  • 需要在 header 的 VM 层中保存一个变量,这个变量名用以动态修改 ngClass,并在所处的页面添加 active 这一 class 名

  • header 的 VM 层需要使用 @Output 这个指令去创建一个 EventEmitter,传输的对象为当前所处的页面

  • app 的 V 层和 VM 层需要动态接受从子组件传来的事件,并且动态渲染对应页面的内容

    动态渲染的部分可以用 ngIf 或是 ngSwitch 实现

分析完了后就可以开始实现了

header

header V 层

这里的修改相对比较简单,主要是 ngClass 的修改,绑定 click 事件这两个,这部分的内容都可以在 [Angular 基础] - 指令(directives) 中查看,修改如下:

<ul class="nav navbar-nav">
  <li [ngClass]="{ active: activeTab === 'recipe' }">
    <a href="#" (click)="onClickTab('recipe')">Recipes</a>
  </li>
  <li [ngClass]="{ active: activeTab === 'shopping-list' }">
    <a href="#" (click)="onClickTab('shopping-list')">Shopping List</a>
  </li>
</ul>
header VM 层

这里主要修改的部分就是:

  • 新建一个变量保存 activeTab
  • 新建一个 EventEmitter 向父组件传输当前的 activeTab
  • 新建一个 clickHandler 去实现上面两个操作

具体代码如下:

export class HeaderComponent {
  @Output() activeTabChanged = new EventEmitter<string>();
  activeTab: string = 'recipe';

  onClickTab(activeTab: string) {
    this.activeTab = activeTab;
    this.activeTabChanged.emit(activeTab);
  }
}

EventEmitter 部分的实现可以查看这篇笔记:[Angular 基础] - 自定义事件 & 自定义属性

app 路由修改

app 路由 V 层修改

V 层的修改相对比较简单,主要是需要将 EventEmitter 绑定一下,并且实现一下条件控制,我这里用 ngSwitch 实现:

<app-header (activeTabChanged)="onTabChange($event)"></app-header>

<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div [ngSwitch]="activeTab">
        <app-recipes *ngSwitchCase="'recipe'"></app-recipes>
        <app-shopping-list *ngSwitchCase="'shopping-list'"></app-shopping-list>
      </div>
    </div>
  </div>
</div>
app 路由 VM 层修改

VM 层实现的功能也差不多,新建一个变量保存 activeTab,随后实现对应的 onTabChange 去修改 activeTab,实现如下:

export class AppComponent {
  title = 'recipe-book';
  activeTab = 'recipe';

  onTabChange($event: string) {
    this.activeTab = $event;
  }
}

最后实现的效果如下:

第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript

recipe-list 清理

recipe-list 部分要清理的比较多:

  • 将单独的 recipe 从 recipe-list 传到 recipe-item 中

    刚开始写的时候还没有涉及到组件之间的沟通,因此子组件无法获取父组件的数据

    现在父子组件已经可以沟通了,那么还是要遵从一下 SRP 的

  • 点击列表中的 recipe,右侧能够显示出 recipe 的具体信息

    这部分就像 wireframe 中的设定一样:

    第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript

传递 recipe 到子组件

recipe-list V 层清理
<a href="#" class="list-group-item clearfix" *ngFor="let recipe of recipes">
  <app-recipe-item [recipe]="recipe"></app-recipe-item>
</a>

这里原本的内容,包括渲染名称、描述、图片都会移到 recipe-item 中去

recipe-item V 层修改

这里主要就是接受之前在 recipe-list V 层的模板

<div class="pull-left">
  <h4 class="list-group-item-heading">{{ recipe.name }}</h4>
  <p class="list-group-item-text">{{ recipe.description }}</p>
</div>
<span class="pull-right">
  <img
    [src]="recipe.imagePath"
    [alt]="recipe.name"
    class="image-responsive"
    style="max-height: 50px"
  />
</span>
recipe-item VM 层修改

这里通过 @Input 去接受父组件传来的数据以完成动态渲染,代码如下:

export class RecipeItemComponent {
  @Input() recipe: Recipe;
}

这里看到样式没有任何的变化,不过 recipe 的渲染部分是由 recipe-item 完成的:

第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript

选择当前展示 recipe

这部分的实现其实和 navigation 的实现差不多,逻辑也基本一样。

看一下目前的项目结构:

❯ tree src/app/
src/app/
├── recipes
│   ├── recipe-detail
│   ├── recipe-list
│   │   ├── recipe-item

只不过这里处理 Output 的是 recipe-list,接受子组件传来的参数是 recipe,并且 recipe 需要将传过来的数据再传递到 recipe-detail 进行展示

⚠️:我这里偷了个懒,直接从 recipe-list 开始往上走。原本的写法应该是所有的东西都放在 recipe-item 里,然后一个个 emit event,因为 Angular 默认情况下是阻止 event propagation 的,所以只能手动一个个往上送(bubbling)

recipe-list V 层添加点击事件
<a
  href="#"
  class="list-group-item clearfix"
  *ngFor="let recipe of recipes"
  [ngClass]="{ active: recipe === selectedRecipe }"
  (click)="onSelectRecipe(recipe)"
>
  <app-recipe-item [recipe]="recipe"></app-recipe-item>
</a>

这里的 a 标签完全是可以丢到 app-recipe-item 去实现的,不过事件一旦传到了 app-recipe-item,就新创建了一个父子之间的沟通,所以需要从 app-recipe-item 再向上 propagate 一个事件,所以这里偷懒了

recipe-list VM 层添加选择事件

这里是对 onSelectRecipe 进行的处理,这里本身也是一个事件的中转站,它需要向 recipes 这一层去发送事件,使得当前被选中的对象可以传到 recipe-detail 中被展示:

export class RecipeListComponent {
  selectedRecipe: Recipe = this.recipes[0];
  @Output() activeRecipeChanged = new EventEmitter<Recipe>();

  ngOnInit() {
    this.activeRecipeChanged.emit(this.recipes[0]);
  }

  onSelectRecipe(recipe: Recipe) {
    this.selectedRecipe = recipe;
    this.activeRecipeChanged.emit(recipe);
  }
}

💡:我这里是用了 ngOnInit 在组件渲染时自动旋转数组中第一个对象,这个根据业务条件设置,也可以不选择这种做法。

recipe V 层添加绑定事件

这里除了新增事件绑定之外,还需要将绑定的对象传到 recipe-detail 中去,主要修改如下:

<div class="row">
  <div class="col-md-5">
    <app-recipe-list
      (activeRecipeChanged)="onRecipeChange($event)"
    ></app-recipe-list>
  </div>
  <div class="col-md-7">
    <app-recipe-detail
      [activeRecipe]="activeRecipe"
      *ngIf="activeRecipe; else noActiveRecipe"
    ></app-recipe-detail>
    <ng-template #noActiveRecipe>
      <p>Please create and select a recipe to view the detailed information</p>
    </ng-template>
  </div>
</div>

这里使用了 ngIf 进行条件控制,预防的是一个边界条件,也就是当数组为空时,recipes[0] 会是一个 undefined,这样会影响 recipe-detail 的后续操作

recipe VM 层添加接受修改数据

VM 层变动不大,主要是接受传递的事件,然后保存传上来的 recipe 即可:

export class RecipesComponent {
  activeRecipe: Recipe;

  onRecipeChange($event: Recipe) {
    this.activeRecipe = $event;
  }
}
recipe-detail

这里主要就是用 @Input 接受一下传来的数据,在 V 层渲染出来即可,变动比较少,代码就都贴下面了,V 层除了变量名基本没有变动:

export class RecipeDetailComponent {
  @Input() activeRecipe: Recipe;
}
<div class="row">
  <div class="col-xs-12">
    <img
      src="{{ activeRecipe.imagePath }}"
      alt=" {{ activeRecipe.name }} "
      class="img-responsive"
    />
  </div>
</div>
<div class="row">
  <div class="col-xs-12">
    <h1>{{ activeRecipe.name }}</h1>
  </div>
</div>
<div class="row">
  <div class="col-xs-12">
    <div class="btn-group">
      <button type="button" class="btn btn-primary dropdown-toggle">
        Manage Recipe <span class="caret"></span>
      </button>
      <ul class="dropdown-menu">
        <li><a href="#">To Shopping List</a></li>
        <li><a href="#">Edit Recipe</a></li>
        <li><a href="#">Delete Recipe</a></li>
      </ul>
    </div>
  </div>
</div>
<div class="row">
  <div class="col-xs-12">{{ activeRecipe.description }}</div>
</div>
<div class="row">
  <div class="col-xs-12">Ingredients</div>
</div>

最后效果如下:

第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript

⚠️:后来发现图片的路径没放进去,HTML 部分更新了,但是动图没更新

shopping-list

这里的应用主要是 local reference 和 @ViewChild,wireframe 中看到有两个输入:

第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript

所以这里两种方式都会用上

导入 FormsModule

不导入 FormsModule 所有的实现都会以默认的 HTML 实现,这里的 button 类型是 submit,也就是说会触发一个提交的 action,一般情况下会导致页面重新刷新

导入 FormsModule 会让 Angular 接收所有传来的参数,并且让 Angular 癌性决定后面的操作

shopping-list V 层修改

这里主要修改的部分就是在两个 input 中加入了 local reference,并且在 submit 的 button 那里绑定了点击时间,并且将 nameInput 作为 reference 传了过去

<div class="row">
  <div class="col-xs-12">
    <form>
      <div class="row">
        <div class="col-sm-5 form-group">
          <label for="name">Name</label>
          <input type="text" id="name" class="form-control" #nameInput />
        </div>
        <div class="col-sm-2 form-group">
          <label for="amount">Amount</label>
          <input type="number" id="amount" class="form-control" #amountInput />
        </div>
      </div>
      <div class="row">
        <div class="col-xs-12">
          <div class="btn-toolbar">
            <button
              class="btn btn-success mr-2"
              type="submit"
              (click)="onAddIngredient(nameInput)"
            >
              Add
            </button>
            <button class="btn btn-danger mr-2" type="button">Delete</button>
            <button class="btn btn-primary" type="button">Edit</button>
          </div>
        </div>
      </div>
    </form>
  </div>
</div>
shopping-list VM 层修改

这里比较简单,使用 @ViewChild 绑定当前 V 层的 local reference,并且接受点击事件传来的 #nameInput,最后按照老规矩,创建一个 EventEmitter 让父组件监听:

export class ShoppingEditComponent {
  @ViewChild('amountInput', { static: true })
  amountInput: ElementRef;
  @Output() ingredientAdded = new EventEmitter<Ingredient>();

  onAddIngredient(nameInput: HTMLInputElement) {
    this.ingredientAdded.emit({
      name: nameInput.value,
      amount: this.amountInput.nativeElement.value,
    });
  }
}
shopping-cart 接受子组件传来的参数

这里也比较简单,就放在一起写了

<div class="row">
  <div class="col-xs-10">
    <app-shopping-edit
      (ingredientAdded)="onAddIngredient($event)"
    ></app-shopping-edit>
    <hr />
    <ul class="list-group">
      <a
        class="list-group-item"
        style="cursor: pointer"
        *ngFor="let ingredient of ingredients"
      >
        {{ ingredient.name }} ({{ ingredient.amount }})
      </a>
    </ul>
  </div>
</div>
import { Component } from '@angular/core';
import { Ingredient } from '../shared/ingredient.model';

@Component({
  selector: 'app-shopping-list',
  templateUrl: './shopping-list.component.html',
  styleUrl: './shopping-list.component.css',
})
export class ShoppingListComponent {
  ingredients: Ingredient[] = [
    new Ingredient('Apples', 5),
    new Ingredient('Tomatoes', 10),
  ];

  onAddIngredient(ingredient: Ingredient) {
    this.ingredients.push(ingredient);
  }
}

最后完成效果如下:

第一个 Angular 项目 - 动态页面,项目,angular.js,javascript,ecmascript


至此一个功能大抵完成的动态页面就写完了文章来源地址https://www.toymoban.com/news/detail-830791.html

到了这里,关于第一个 Angular 项目 - 动态页面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端项目部署自动检测更新后通知用户刷新页面(前端实现,技术框架vue、js、webpack)——方案一:编译项目时动态生成一个记录版本号的文件

    当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。 vue、js、webpack 编译项目时动态生成一个记录版本号的文件 轮询(20s、自己设定时间)这个文件,判断版

    2024年02月02日
    浏览(64)
  • Angular:动态依赖注入和静态依赖注入

    问题描述: 自己写的服务依赖注入到组件时候是直接在构造器内初始化的。 直到看见代码中某大哥写的 private injector: Injector   在 Angular 中,使用构造函数注入的方式将服务注入到组件中是一种静态依赖注入的方式。这种方式需要在组件的构造函数中显式声明该服务的类型,

    2024年02月15日
    浏览(48)
  • angular中如何定义一个全局组件?

    需求,我们需要新建一个navBreadcrumb的全局组件。这是一个面包屑导航,在不同的页面引入时传入一个路由数组即可。 第一步:我们新建这个组件: 这里有几个要注意的点: 1 ---nav-breadcrumb.module----------这个文件时一定要的。angular和vue/react不同,modulecomponent,对于任何页面而

    2024年02月12日
    浏览(40)
  • Angular开发之——安装Angular CLI并初始化项目(02)

    Angular CLI介绍及开发环境准备 安装Angular CLI开发工具 使用Angular CLI初始化Angular项目 简单体验Angular 2.1 Angular CLI介绍 目前,无论你使用什么前端框架,都必须要使用到各种NodeJS工具,Angular也不例外。与其他框架不同,Angular从一开始就走的“全家桶”式的设计思路,因此@angul

    2024年02月07日
    浏览(36)
  • 二甲医院信息管理系统源码 his系统源码 java+Angular+JavaScript

    云HIS系统采用SaaS软件应用服务模式,提供软件应用服务多租户机制,实现一中心部署多机构使用。主要包含收费计费、药品管理、门诊医生工作站、住院医生工作站、护士工作站、数据统计、电子病历、医保接口等功能,能够满足医院及诊所日常业务开展的需要。     设计

    2024年02月11日
    浏览(45)
  • 关于 Angular 和 Node.js 版本的问题

    只有 Angular 12 才支持 Node.js 16 吗?Angular 10 呢? StackOverflow 有朋友抱怨关于 Angular 和 Node.js 版本的问题: 我的实际工作中,经常不得不启动旧的 Angular 项目,其中包含已弃用的 Angular 依赖项。 因为我经常运行最新的 Node.js 版本(至少是最新的 LTS 版本),所以我经常遇到无法

    2024年02月04日
    浏览(48)
  • angular——子组件如何接收父组件的动态传值

    开发过程中,父组件给子组件传值的情况很常见,今天我们就来聊聊父组件给子组件传值可能会发生哪些意外,什么情况下子组件无法接收到父组件最新的传值; 基本数据类型 :父组件给子组件传递 基本数据类型 ,子组件使用变量接收传值;当传值发生变化,子组件接收的

    2024年02月14日
    浏览(42)
  • Angular系列教程之zone.js和NgZone

    在Angular中,zone.js是一个非常重要的库,它为我们提供了一种跟踪和管理异步操作的机制。它的核心概念是Zone,它可以帮助我们捕获和处理异步操作的上下文。 当我们执行异步操作(例如定时器、网络请求或者订阅Observables)时,往往需要确保这些操作可以正确地传播并影响

    2024年01月17日
    浏览(42)
  • 一文了解Angular、React 和 Vue.js的区别

    前端开发人员在开始一个新项目时首先要回答的问题是:我应该选择哪个框架? 哪个框架更适合我的需求? 在本文中,我们将向您快速概述当前使用的最常见的前端框架,旨在帮助您选择最能满足您需求的框架。这些框架是 Angular、React和 Vue.js。   一、介绍 Angular是一个包

    2024年02月08日
    浏览(55)
  • Electron 和 Angular 项目升级

    原项目 Angular 和 Electron 版本: @angular/cli: 1.4.9 @angular/core: 4.4.6 Electron: 1.7.8 升级后 Angular 和 Electron 版本: Angular: 13.3.1 Electron: 21.2.1 流程: angular-electron 这是一个结合 Angular 和 Electron 的项目。以此为基准环境,不需要从头构建。这个项目的 Angular13 对应的 electron 为 v18,再手动到升

    2024年02月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包