Getting started with Angular 2: part 5

Authenticiaton

We follow the steps in Angular 1.x sample. I will create some facilities.

  1. A Signin and Signup components.
  2. A AuthService to wrap HTTP authentiction service.
  3. A AppShowAuthed directive to show or hide UI element against the authentiction status.

Lets start with creating AuthService.

AuthService

In order to handle signin and signup request,create a service named AuthService in src/app/core folder to process it.

@Injectable()
export class AuthService {

  private currentUser$: BehaviorSubject<User> = new BehaviorSubject<User>(null);
  private authenticated$: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);
  private desiredUrl: string = null;

  constructor(
    private api: ApiService,private jwt: JWT,private router: Router) {
  }

  attempAuth(type: string,credentials: any) {
    const path = (type === 'signin') ? '/login' : '/signup';
    const url = '/auth' + path;

    this.api.post(url,credentials)
      .map(res => res.json())
      .subscribe(res => {
        this.jwt.save(res.id_token);
        // set Authorization header
        this.setJwtHeader(res.id_token);

        this.setState(res.user);

        if (this.desiredUrl && !this.desiredUrl.startsWith('/signin')) {
          const _targetUrl = this.desiredUrl;
          this.desiredUrl = null;
          this.router.navigateByUrl(_targetUrl);
        } else {
          this.router.navigate(['']);
        }
      });
  }



  logout() {
    // reset the initial values
    this.setState(null);
    //this.desiredUrl = null;

    this.jwt.destroy();
    this.clearJwtHeader();
    this.desiredUrl = null;

    this.router.navigate(['']);
  }

  currentUser(): Observable<User> {
    return this.currentUser$.distinctUntilChanged();
  }

  isAuthenticated(): Observable<boolean> {
    return this.authenticated$.asObservable();
  }

  getDesiredUrl() {
    return this.desiredUrl;
  }

  setDesiredUrl(url: string) {
    this.desiredUrl = url;
  }

  private setState(state: User) {
    if (state) {
      this.currentUser$.next(state);
      this.authenticated$.next(true);
    } else {
      this.currentUser$.next(null);
      this.authenticated$.next(false);
    }
  }

  private setJwtHeader(jwt: string) {
    this.api.setHeaders({ Authorization: `Bearer ${jwt}` });
  }

  private clearJwtHeader() {
    this.api.deleteHeader('Authorization');
  }
}

In the constructor method,we inject the ApiService we had created in the last post,and a JWT utility and a Router.

attempAuth method is use for handling signin and signup requests. It accpets two parameters,type and credentials data.

If signin or signup is handled sucessfully,save the token value into localStorage by JWT service and set Http Header Authenticiaton in ApiService,then navigate to the desired URL.

The JWT is just a simple class to fetch jwt token from localStorage and save jwt into localStorage.

const JWT_KEY: string = 'id_token';

@Injectable()
export class JWT {

  constructor(/*@Inject(APP_CONFIG) config: AppConfig*/) {
    //this.jwtKey = config.jwtKey;
  }

  save(token) {
    window.localStorage[JWT_KEY] = token;
  }

  get() {
    return window.localStorage[JWT_KEY];
  }

  destroy() {
    window.localStorage.removeItem(JWT_KEY);
  }

}

Do not forget to register AuthService and JWT in core.module.ts.

@NgModule({
	...
  providers: [
    ...
    AuthService,JWT,...
  ]
})
export class CoreModule {
}

Create Signin and Signup UI components

Generate signin and signup components via ng g component command.

Execute the following commands in the project root folder.

ng g component signin
ng g module signin 

ng g component signup
ng g module signup

Signin

The content of SigninComponent.

import { Component,OnInit,OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

import { AuthService } from '../core/auth.service';

@Component({
  selector: 'app-signin',templateUrl: './signin.component.html',styleUrls: ['./signin.component.css']
})
export class SigninComponent implements OnInit,OnDestroy {

  data = {
    username: '',password: ''
  };

  sub: Subscription;

  constructor(private authServcie: AuthService) { }

  ngOnInit() { }

  submit() {
    console.log('signin with credentials:' + this.data);
    this
      .authServcie
      .attempAuth('signin',this.data);

  }

  ngOnDestroy() {
    //if (this.sub) { this.sub.unsubscribe(); }
  }

}

It is easy to understand,just call AuthService attempAuth method and set type parameter value to signin.

Lets have a look at the template file.

<div class="row">
  <div class="offset-md-3 col-md-6">
    <div class="card">
      <div class="card-header">
        <h1>{{'signin' }}</h1>
      </div>
      <div class="card-block">
        <form #f="ngForm" id="form" name="form" class="form" (ngSubmit)="submit()" novalidate>
          <div class="form-group" [class.has-danger]="username.invalid && !username.pristine">
            <label class="form-control-label" for="username">{{'username'}}</label>
            <input class="form-control" id="username" name="username" #username="ngModel" [(ngModel)]="data.username" required/>
            <div class="form-control-feedback" *ngIf="username.invalid && !username.pristine">
              <p *ngIf="username.errors.required">Username is required</p>
            </div>
          </div>
          <div class="form-group" [class.has-danger]="password.invalid && !password.pristine">
            <label class="form-control-label" for="password">{{'password'}}</label>
            <input class="form-control" type="password" name="password" id="password" #password="ngModel" [(ngModel)]="data.password" required/>
            <div class="form-control-feedback" *ngIf="password.invalid && !password.pristine">
              <p ng-message="password.errors.required">Password is required</p>
            </div>
          </div>
          <div class="form-group">
            <button type="submit" class="btn btn-success btn-lg" [disabled]="f.invalid || f.pending">  {{'signin'}}</button>
          </div>
        </form>
      </div>
      <div class="card-footer">
        Not registered,<a [routerLink]="['','signup']">{{'signup'}}</a>
      </div>
    </div>
  </div>
</div>

In Signup component,we use template driven form to process form submission.

username and password are required fields,if the input value is invalid,the error messages will be displayed.

Declare SigninComponent in signin.module.ts.

import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { SigninRoutingModule } from './signin-routing.module';
import { SigninComponent } from './signin.component';

@NgModule({
  imports: [
    SharedModule,SigninRoutingModule
  ],declarations: [SigninComponent]
})
export class SigninModule { }

Defind routing rule in signin-routing.module.ts.

import { NgModule } from '@angular/core';
import { Routes,RouterModule } from '@angular/router';
import { SigninComponent } from './signin.component';

const routes: Routes = [
  { path: '',component: SigninComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],exports: [RouterModule],providers: []
})
export class SigninRoutingModule { }

Mount signin routing in AppRoutingModule.

const routes: Routes = [
  ...
  { path: 'signin',loadChildren: 'app/signin/signin.module#SigninModule' },...
];

Signup

The content of SignupComponent.

import { Component,OnInit } from '@angular/core';
import { FormBuilder,FormGroup,FormControl,Validators } from '@angular/forms';
import { AuthService } from '../core/auth.service';

@Component({
  selector: 'app-signup',templateUrl: './signup.component.html',styleUrls: ['./signup.component.css']
})
export class SignupComponent implements OnInit {

  signupForm: FormGroup;
  firstName: FormControl;
  lastName: FormControl;
  email: FormControl;
  username: FormControl;
  password: FormControl;
  passwordConfirm: FormControl;
  passwordGroup: FormGroup;

  constructor(private authService: AuthService,private fb: FormBuilder) {
    this.firstName = new FormControl('',[Validators.required]);
    this.lastName = new FormControl('',[Validators.required]);
    this.email = new FormControl('',[Validators.required,this.validateEmail]);
    this.username = new FormControl('',Validators.minLength(6),Validators.maxLength(20)]);
    this.password = new FormControl('',Validators.maxLength(20)]);
    this.passwordConfirm = new FormControl('',Validators.maxLength(20)]);

    this.passwordGroup = fb.group(
      {
        password: this.password,passwordConfirm: this.passwordConfirm
      },{ validator: this.passwordMatchValidator }
    );

    this.signupForm = fb.group({
      firstName: this.firstName,lastName: this.lastName,email: this.email,username: this.username,passwordGroup: this.passwordGroup
    });
  }

  passwordMatchValidator(g: FormGroup) {
    return g.get('password').value === g.get('passwordConfirm').value
      ? null : { 'mismatch': true };
  }

   validateEmail(c: FormControl) {
     let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;

     return EMAIL_REGEXP.test(c.value) ? null : {
       validateEmail: {
         valid: false
       }
     };
   }

  ngOnInit() {
  }

  submit() {
    console.log('saving signup form data@' + this.signupForm.value);
    let value = this.signupForm.value;
    let data = {
      firstName: value.firstName,lastName: value.lastName,username: value.username,password: value.passwordGroup.password
    };
    this.authService.attempAuth('signup',data);
  }

}

Unlikes SigninComponent,we use programatic approach to setup signup form,declare form controls,and set the validators.

There are serveral built-in validors in Validators,such as required,minLength,maxLength etc.

Please note we create a custom validation method(validateEmail) for email control,it accpets a FormControl as parameter.

Another custom validation method is passwordMatchValidator,it is designated for a composite components to check if password and passwordConfirm are matched.

Let's have a look at the template file,src/app/signup.component.html.

<div class="row">
  <div class="offset-md-3 col-md-6">
    <div class="card">
      <div class="card-header">
        <h1>{{ 'signup' }}</h1>
      </div>
      <div class="card-block">
        <form id="form" name="form" class="form" [formGroup]="signupForm" (ngSubmit)="submit()" novalidate>
          <div class="row">
            <div class="col-md-6">
              <div class="form-group" [class.has-danger]="firstName.invalid && !firstName.pristine">
                <label class="form-control-label" for="firstName">{{'firstName'}}</label>
                <input class="form-control" id="firstName" name="firstName" [formControl]="firstName"/>
                <div class="form-control-feedback"  *ngIf="firstName.invalid && !firstName.pristine">
                  <p *ngIf="firstName.errors.required">FirstName is required</p>
                </div>
              </div>
            </div>
            <div class="col-md-6">
              <div class="form-group" [class.has-danger]="lastName.invalid && !lastName.pristine">
                <label class="form-control-label col-md-12" for="lastName">{{'lastName'}}</label>
                <input class="form-control" id="lastName" name="lastName" [formControl]="lastName"/>
                <div class="form-control-feedback"  *ngIf="lastName.invalid && !lastName.pristine">
                  <p *ngIf="lastName.errors.required">LastName is required</p>
                </div>
              </div>
            </div>
          </div>

           <div class="form-group" [class.has-danger]="email.invalid && !email.pristine">
            <label class="form-control-label" for="email">{{'email'}}</label>
            <input class="form-control" id="email" name="email" type="email" [formControl]="email"/>
            {{email.errors|json}}
            <div class="form-control-feedback" *ngIf="email.invalid && !email.pristine">
              <p *ngIf="email.errors.required">email is required</p>
            </div>
          </div>

          <div class="form-group" [class.has-danger]="username.invalid && !username.pristine">
            <label class="form-control-label" for="username">{{'username'}}</label>
            <input class="form-control" id="username" name="username" type="text" [formControl]="username"/>
            <div class="form-control-feedback" *ngIf="username.invalid && !username.pristine">
              <p *ngIf="username.errors.required">Username is required</p>
              <p *ngIf="username.errors.minlength">Username is too short(at least 6 chars)</p>
              <p *ngIf="username.errors.maxlength">Username is too long(at most 20 chars)</p>
            </div>
          </div>
          <div class="row" [formGroup]="passwordGroup">
              <div class="col-md-12">
                <div class="form-group" [class.has-danger]="password.invalid && !password.pristine">
                  <label class="form-control-label" for="password">{{'password'}}</label>
                  <input class="form-control" type="password" name="password" id="password" [formControl]="password" />
                  <div class="form-control-feedback"  *ngIf="password.invalid && !password.pristine">
                    <p *ngIf="password.errors.required">Password is required.</p>
                    <p *ngIf="password.errors.minlength||password.errors.maxlength">Password should be consist of 6 to 20 characters.</p>
                  </div>
                </div>
            </div>
            <div class="col-md-12">
               <div class="form-group" [class.has-danger]="passwordConfirm.invalid && !passwordConfirm.pristine">
                <label class="form-control-label" for="passwordConfirm">{{'passwordConfirm'}}</label>
                <input class="form-control" type="password" name="passwordConfirm" id="passwordConfirm" [formControl]="passwordConfirm" />
                <div class="form-control-feedback"  *ngIf="passwordConfirm.invalid && !passwordConfirm.pristine">
                  <p *ngIf="passwordConfirm.errors.required">passwordConfirm is required.</p>
                  <p *ngIf="passwordConfirm.errors.minlength||passwordConfirm.errors.maxlength">passwordConfirm should be consist of 6 to 20 characters.</p>
                </div>
              </div>
            </div>
            <div class="col-md-12" [class.has-danger]="passwordGroup.invalid">
              <div class="form-control-feedback" *ngIf="passwordGroup.invalid">
                <p *ngIf="passwordGroup.hasError('mismatch')">Passwords are mismatched.</p>
              </div>
            </div>
          </div>

          <div class="form-group">
            <button type="submit" class="btn btn-success btn-lg" [disabled]="signupForm.invalid || signupForm.pending">  {{'SIGN UP'}}</button>
          </div>
        </form>
      </div>
      <div class="card-footer">
        Already resgistered,'signin']">{{'signin'}}</a>
      </div>
    </div>
  </div>
</div>

Like Signin template file,it is simple and stupid,the different we use property binding(formGroup and formControl) to backing component class.

Declare SignupComponent in src/app/signup.module.ts.

import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { SignupRoutingModule } from './signup-routing.module';
import { SignupComponent } from './signup.component';

@NgModule({
  imports: [
    SharedModule,SignupRoutingModule
  ],declarations: [SignupComponent]
})
export class SignupModule { }

And define signup routing rules in src/app/signup/signup-routing.module.ts.

import { NgModule } from '@angular/core';
import { Routes,RouterModule } from '@angular/router';

import { SignupComponent } from './signup.component';

const routes: Routes = [
  { path: '',component: SignupComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],providers: []
})
export class SignupRoutingModule { }

And mount it in src/app/app-routing.module.ts.

const routes: Routes = [
	...
	{ path: 'signup',loadChildren: 'app/signup/signup.module#SignupModule' },...

Make sure the links of signin and siginup are added in NavbarComponent.

<ul class="navbar-nav my-2 my-lg-0">
	<li class="nav-item"><a class="btn btn-outline-success" [routerLink]="['/signin']" >{{'signin'}}</a></li>
	<li class="nav-item"><a class="nav-link" [routerLink]="['/signup']"  >{{'signup'}}</a>
	</li>
</ul>

If your project is running,you can try signin and signup in browers.

Email validator

In the SignupComponent,we used a method to validate email field value. We can use a standalone Email validator to implement it and thus validator can be resued in other case.

Create a directive in src/app/shared folder.

import { Directive,forwardRef } from '@angular/core';
import { NG_VALIDATORS,FormControl } from '@angular/forms';

function validateEmailFactory(/* emailBlackList: EmailBlackList*/) {
  return (c: FormControl) => {
    let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;

    return EMAIL_REGEXP.test(c.value) ? null : {
      validateEmail: {
        valid: false
      }
    };
  };
}
//http://blog.thoughtram.io/angular/2016/03/14/custom-validators-in-angular-2.html
//http://blog.ng-book.com/the-ultimate-guide-to-forms-in-angular-2/
@Directive({
  selector: '[validateEmail][ngModel],[validateEmail][formControl],[validateEmail][formControlName]',providers: [
    { provide: NG_VALIDATORS,useExisting: forwardRef(() => EmailValidatorDirective),multi: true }
  ]
})
export class EmailValidatorDirective {

  validator: Function;

  constructor(/*emailBlackList: EmailBlackList*/) {
    this.validator = validateEmailFactory();
  }

  validate(c: FormControl) {
    return this.validator(c);
  }

}

Declare it in shared.module.ts.

...
import { EmailValidatorDirective } from './email-validator.directive';


@NgModule({
	...
  declarations: [
	...
    EmailValidatorDirective
  ],exports: [
    ...
    EmailValidatorDirective,],})
export class SharedModule { }

In SignupModule we have imported SharedModule.

Apply it in signup template.

<div class="form-group" [class.has-danger]="email.invalid && !email.pristine">
<label class="form-control-label" for="email">{{'email'}}</label>
<input class="form-control" id="email" name="email" type="email" [formControl]="email" validateEmail/>
{{email.errors|json}}
<div class="form-control-feedback" *ngIf="email.invalid && !email.pristine">
  <p *ngIf="email.errors.required">email is required</p>
  <p *ngIf="email.errors.validateEmail">Email is invalid</p>
</div>
</div>

Remove the declaration of email control in SignupComponent and validateEmail method;.

this.email = new FormControl('',[Validators.required]);

Remember authentication status

We have saved token in localStorage when the user is authenticated,but user return back to the application,the saved token should be reused and avoid to force user to login again.

Create a method in AuthService to verify the token in localStorage.

verifyAuth(): void {

    // jwt token is not found in local storage.
    if (this.jwt.get()) {

      // set jwt header and try to refresh user info.
      this.setJwtHeader(this.jwt.get());

      this.api.get('/me').subscribe(
        res => {
          this.currentUser$.next(res);
          this.authenticated$.next(true);
        },err => {
          this.clearJwtHeader();
          this.jwt.destroy();
          this.currentUser$.next(null);
          this.authenticated$.next(false);
        }
      );
    }
  }

If the token is existed,refresh the user info and store them in AuthService,else if it is failed for some reason,such as token is expired,it will clean token in localStorage and force you to be authenticated for protected resource.

Add the following codes in app.component.ts,it will verifyAuth in component lifecycle hook OnInit when the application is started.

export class AppComponent implements OnInit {
  title = 'app works!';

  constructor(private authService: AuthService) {
  }

  ngOnInit() {
    this.authService.verifyAuth();
  }
}

Protect resource when navigation

Some resource like edit post,new post etc,user authentication are required. When use click these links,it try to navigate to the target page,the application should stop it and force user to authenticate in the login page.

Angular provides some hooks in routing stage,such as CanActivate,CanDeactivate,CanLoad,Resolve etc.

Create CanActivate service to check if user is authenticated.

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router,private authService: AuthService) { }

  canActivate(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): boolean {
    console.log('route.log' + route.url);
    console.log('state' + state.url);

    this
      .authService
      .isAuthenticated()
      .subscribe((auth) => {
        if (!auth) {
          this.authService.setDesiredUrl(state.url);
          console.log('desiredUrl@' + state.url);
          this
            .router
            .navigate(['','signin']);
        }
      });
    return true;
  }
}

Apply it in PostsRoutingModule.

const routes: Routes = [
  ...
  { path: 'new',component: NewPostComponent,canActivate: [AuthGuard] },{ path: 'edit/:id',component: EditPostComponent,...
];

In the AuthGuard,if user is not authenticated,the target URL will be remembered.

this.authService.setDesiredUrl(state.url);

And when user is authenticated sucessfully via attempAuth method,it will try to navigate to the desiredUrl.

if (this.desiredUrl && !this.desiredUrl.startsWith('/signin')) {
  const _targetUrl = this.desiredUrl;
  this.desiredUrl = null;
  this.router.navigateByUrl(_targetUrl);
} else {
  this.router.navigate(['']);
}

Protect content in page

Some content fragment is sensitive for security and only show for authencatied user. Create a directive to show or hide content according to user's authencation status.

In src/app/shared/ folder,create a directive named show-authed.directive.ts.

import { Directive,ElementRef,Input,Renderer,HostBinding,Attribute,OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { AuthService } from '../core/auth.service';

@Directive({
  selector: '[appShowAuthed]'
})
export class ShowAuthedDirective implements OnInit,OnDestroy {

  @Input() appShowAuthed: boolean;
  sub: Subscription;

  constructor(private authService: AuthService,private el: ElementRef,private renderer: Renderer) {
    console.log('[appShowAuthed] value:' + this.appShowAuthed);
  }

  ngOnInit() {
    console.log('[appShowAuthed] ngOnInit:');
    this.sub = this.authService.currentUser().subscribe((res) => {
      if (res) {
        if (this.appShowAuthed) {
          this.renderer.setElementStyle(this.el.nativeElement,'display','inherit');
        } else {
          this.renderer.setElementStyle(this.el.nativeElement,'none');
        }

      } else {
        if (this.appShowAuthed) {
          this.renderer.setElementStyle(this.el.nativeElement,'none');
        } else {
          this.renderer.setElementStyle(this.el.nativeElement,'inherit');
        }
      }
    });
  }

  ngOnDestroy() {
    console.log('[appShowAuthed] ngOnDestroy:');
    if (this.sub) { this.sub.unsubscribe(); }
  }

}

Declare it in SharedModule.

import { ShowAuthedDirective } from './show-authed.directive';

@NgModule({
  declarations: [
    ...
	ShowAuthedDirective],exports: [
	...
	ShowAuthedDirective]
  }
})
export class SharedModule { }

Now change the navbar,show login and sign up link when user is not authencatied,and show a logout link when user is authencated.

<ul class="navbar-nav my-2 my-lg-0">
	<li class="nav-item" [appShowAuthed]="false"><a class="btn btn-outline-success" [routerLink]="['/signin']" >{{'signin'}}</a></li>
	<li class="nav-item" [appShowAuthed]="false"><a class="nav-link" [routerLink]="['/signup']"  >{{'signup'}}</a>
	</li>
	<li class="nav-item" [appShowAuthed]="true"><button type="button" class="btn btn-outline-danger" (click)="logout()">{{'logout'}}</button>
	</li>
</ul>

And the post details page,use a login button instead of the comment form when user is not authencated.

<div [appShowAuthed]="true">
  <app-comment-form (saved)="saveComment($event)"></app-comment-form>
</div>
<div class="mx-auto my-2" [appShowAuthed]="false">
  <a class="btn btn-lg btn-success" routerLink="/signin">SING IN</a>
</div>

Now the authentication flow is done.

Source codes

Check out the source codes from Github and play yourself.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


ANGULAR.JS:NG-SELECTANDNG-OPTIONSPS:其实看英文文档比看中文文档更容易理解,前提是你的英语基础还可以。英文文档对于知识点讲述简明扼要,通俗易懂,而有些中文文档读起来特别费力,基础差、底子薄的有可能一会就会被绕晕了,最起码英文文档中的代码与中文文档中的代码是一致的,但知识点讲述实在是差距太大。Angular.jshasapowerfuldire
AngularJS中使用Chart.js制折线图与饼图实例  Chart.js 是一个令人印象深刻的 JavaScript 图表库,建立在 HTML5 Canvas 基础上。目前,它支持6种图表类型(折线图,条形图,雷达图,饼图,柱状图和极地区域区)。而且,这是一个独立的包,不依赖第三方 JavaScript 库,小于 5KB。   其中用到的软件:   Chart.js框架,版本1.0.2,一
IE浏览器兼容性后续前言 继续尝试解决IE浏览器兼容性问题,结局方案为更换jquery、angularjs、IE的版本。 1.首先尝试更换jquery版本为1.7.2 jquery-1.9.1.js-->jquery-1.7.2.js--> jquery2.1.4.js 无效 2.尝试更换IE版本IE8 IE11-
Angular实现下拉菜单多选写这篇文章时,引用文章地址如下:http://ngmodules.org/modules/angularjs-dropdown-multiselecthttp://dotansimha.github.io/angularjs-dropdown-multiselect/#/AngularJSDropdownMultiselectThisdire
在AngularJS应用中集成科大讯飞语音输入功能前言 根据项目需求,需要在首页搜索框中添加语音输入功能,考虑到科大讯飞语音业务的强大能力,遂决定使用科大讯飞语音输入第三方服务。软件首页截图如下所示: 涉及的源代码如下所示: //语音识别$rootScope.startRecognize = function() {var speech;
Angular数据更新不及时问题探讨前言 在修复控制角标正确变化过程中,发觉前端代码组织层次出现了严重问题。传递和共享数据时自己使用的是rootScope,为此造成了全局变量空间的污染。根据《AngularJs深度剖析与最佳实践》,如果两个控制器的协作存在大量的数据共享和交互可以利用Factory等服务的“单例”特性为它们注入一个共享对象来传递数据。而自己在使用rootScope
HTML:让表单、文本框只读,不可编辑的方法有时候,我们希望表单中的文本框是只读的,让用户不能修改其中的信息,如使中国">的内容,"中国"两个字不可以修改。实现的方式归纳一下,有如下几种。方法1:onfocus=this.blur()中国"onfocus=this.blur()>方法2:readonly中国"readonly>中国"readonly="tru
在AngularJS应用中实现微信认证授权遇到的坑前言 项目开发过程中,移动端新近增加了一个功能“微信授权登录”,由于自己不是负责移动端开发的,但最后他人负责的部分未达到预期效果。不能准确实现微信授权登录。最后还得靠自己做进一步的优化工作,谁让自己是负责人呢?原来负责人就是负责最后把所有的BUG解决掉。 首先,熟悉一下微信授权部分的源代码,如下所示:
AngularJS实现二维码信息的集成思路需求 实现生成的二维码包含订单详情信息。思路获取的内容数据如下: 现在可以获取到第一级数据,第二级数据data获取不到。利用第一级数据的获取方法获取不到第二级数据。for(i in data){alert(i); //获得属性 if(typeof(data[i]) == "o
Cookie'data'possiblynotsetoroverflowedbecauseitwastoolarge(5287>4096bytes)!故事起源 项目开发过程中遇到以上问题,刚开始以为只是个警告,没太在意。后来发现直接影响到了程序的执行效果。果断寻找解决方法。问题分析 根据Chrome浏览器信息定位,显示以下代码存在错误:
AngularJS控制器controller之间如何通信angular控制器通信的方式有三种:1,利用作用域继承的方式。即子控制器继承父控制器中的内容2,基于事件的方式。即$on,$emit,$boardcast这三种方式3,服务方式。写一个服务的单例然后通过注入来使用利用作用域的继承方式由于作用域的继承是基于js的原型继承方式,所以这里分为两种情况,当作用域上面的值
AngularJS路由问题解决遇到了一个棘手的问题:点击优惠详情时总是跳转到药店详情页面中去。再加一层地址解决了,但是后来发现问题还是来了:Couldnotresolve'yhDtlMaintain/yhdetail'fromstate'yhMaintain'药店详情http://192.168.1.118:8088/lmapp/index.html#/0优惠券详情
书海拾贝之特殊的ng-src和ng-href在说明这两个指令的特殊之前,需要先了解一下ng的启动及执行过程,如下:1)浏览器加载静态HTML文件并解析为DOM;2)浏览器加载angular.js文件;3)angular监听DOMContentLoaded事件,监听到时开始启动;4)angular寻找ng-app指令,确定作用范围;
angularjs实现页面跳转并进行参数传递Angular页面传参有多种办法,我在此列举4种最常见的:1.基于ui-router的页面跳转传参(1)在AngularJS的app.js中用ui-router定义路由,比如现在有两个页面,一个页面(producers.html)放置了多个producers,点击其中一个目标,页面跳转到对应的producer页,同时将producerId
AngularJS实现表格数据的编辑,更新和删除效果实现首先,我们先建立一些数据,当然你可以从你任何地方读出你的数据var app = angular.module('plunker', ['ui.bootstrap']);app.controller('MainCtrl', function($scope) { $scope.name = 'World'; $sc
ANGULAR三宗罪之版本陷阱      坑!碰到个大坑,前面由于绑定日期时将angular版本换为angular-1.3.0-beta.1时,后来午睡后,登录系统,发现无论如何都登陆不进去了,经过调试,发现数据视图已经无法实现双向绑定了。自己还以为又碰到了“僵尸程序”了呢,对比药店端的程序发现并没有什么不同之处。后来自己经过一番思索才隐约感觉到是不是angular的版本造成的,将版本换为之前
JS实现分页操作前言 项目开发过程中,进行查询操作时有可能会检索出大量的满足条件的查询结果。在一页中显示全部查询结果会降低用户的体验感,故需要实现分页显示效果。受前面“JS实现时间选择插件”的启发,自己首先需要查看一下HTML5能否实现此效果。 整了半天,不管是用纯CSS3也好,还是用tmpagination.js还是bootstrap组件也好,到最后自己静下心来理
浏览器兼容性解决之道前言 浏览器兼容性一直是前端开发中不得不面对的一个问题。而最突出的就是IE。对绝大多数公司来说,兼容IE6的性价比已经很低,而IE7则几乎已经绝迹。所以,常见的兼容性下限是IE8。这也正是Angular1.2x的兼容性目标,Angular团队声明:Angular的持续集成服务器会在IE8下运行所有的测试。但这些测试不会运行在IE7及以下版本,它们也不会保证An
JS利用正则表达式校验手机号绪 由于项目需求,需要在前端实现手机号码的校验。当然了,对于基本的格式校验应该放在客户端进行,而不需要再将待校验的手机号发送至服务端,在服务端完成校验,然后将校验结果返回给客户端,客户端根据返回的结果再进行进一步的处理。如此反而复杂化了处理过程。 其实,处于安全考虑,应该在服务端进行二次校验。以下为在客户端的JS中校验手机号码格式
基于项目实例解析ng启动加载过程前言 在AngularJS项目开发过程中,自己将遇到的问题进行了整理。回过头来总结一下angular的启动过程。 下面以实际项目为例进行简要讲解。1.载入ng库2.等待,直到DOM树构造完毕。3.发现ng-app,自动进入启动引导阶段。4.根据ng-app名称找到相应的路由。5.加载默认地址。6.Js顺序执行,加载相应模版页sys_tpls/