Tag Archives: MVC

Angular 2 Tutorial – movies App

Last time I wrote a blog created the MovieAPI and now I am using that API as back end services to create an angular 2 app.

the final source code could find at my github repository.  here is the link download

For the MovieAPI open it, build it will install the packages automatically, then run the service first.

After the service is done, go to the angular2 folder, open command window

–run npm install,

— npm start

It will look like this:

home

create.PNG

added

Step 1 : Setting up your angular 2 application, I already have a blog  click here to found it. Or also could download from the source file from https://github.com/angular/quickstart/blob/master/README.md but it add a lot of unnecessary setting, probably confuse for beginning learner.

It was better do few times manually setting it up to get understand all of the necessary for start anguar 2 application.

Step 2:  In angualr 2 everything is component, for me normally like to create the service first. I just create a file named movies.service.ts under folder movies


import {Injectable} from 'angular2/core';
import {IMovie} from './movie';
import {Http, RequestOptions , Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';

@Injectable() 
export class MovieService {   
    private _movieUrl = 'http://localhost:53871/api/movies';

    constructor(private _http: Http) { }

    getMovies(): Observable<IMovie[]> {
        return this._http.get(this._movieUrl)
            .map((response: Response) => <IMovie[]>response.json()) //arrow function, tranceform response to json object and is array of IMovie[]
            .do(data => console.log("All: " + JSON.stringify(data))) //for debug
            .catch(this.handleError);
    }


    createMovie(movie: IMovie): Observable {
        let body = JSON.stringify(movie); 
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this._http.post(this._movieUrl, body, options)               
                .map(this.extractData)
                .catch(this.handleError);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body.data || {};
    }

    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

In there MovieService I have a IMovie here is my movie.ts I put it in the same folder with MovieService .


//interface won't compile to js file, it just for typescript do type checking
 export interface IMovie { 
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];

}


export class Movie implements IMovie {
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];
}

Step 3: Inject the services.
Go to the app.component.ts this is our container component, for anguar 2 only need inject once, then all of the nest container could using it ( by import it ).

Inject a service has two steps:

1) import the file into the app.component,

2) Inject it as providers

here is my app.component.ts

angular2_injection

Step 4: Create the movies-list.component.ts


//for using component, simply import it into this file
import {Component, OnInit} from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import {IMovie} from './movie';
import {MovieService} from './movies.service';
import {MovieFilterPipe} from './movie-filter.pipe';
import { StarComponent } from '../shared/star.component';

@Component({
    selector: 'sz-movies',
    templateUrl: 'app/movies/movies-list.component.html',
    styleUrls: ['app/movies/movies-list.component.css'],  
    pipes: [MovieFilterPipe],
    directives: [StarComponent, ROUTER_DIRECTIVES]
   
})
export class MovieListComponent implements OnInit {
    pageTitle: string = 'Movie List';   
   
    listFilter: string;
    errorMessage: string;
    movies: IMovie[];


    constructor(private _movieService: MovieService) {

    }
    
    ngOnInit(): void {
        this._movieService.getMovies()
            .subscribe(
            movies => this.movies = movies,
            error => this.errorMessage = error);
    }

}

here is the movies-list.component.htmlmovies_html

 

for this component I have a nested StarComponent.

Setp 5: create nested StarComponent, here is the star.component.ts under the share folder


import { Component, OnInit, Input} from 'angular2/core';
@Component({
    selector: 'sz-star',
    templateUrl: 'app/shared/star.component.html',
    styleUrls: ['app/shared/star.component.css']
})
export class StarComponent implements OnInit {
    @Input() rating: number;
    starWidth: number;
    ngOnInit(): void {
        this.starWidth = this.rating * 86 / 5;
    }
}

star.component.htmlstar.PNG

css:

star_css

In angular 2: data flow is unidirectional, for passing data from view to the class

first: you passing data into the directive could bind it to the property

passing_data_down.PNG

Second: need import the Input from angular2/core, and bind it to the property

inData

Now, one nested movies-list.component and nested star.componet down.

Now using the same steps create the MovieCreateComponent


import {Component, OnInit} from 'angular2/core';

import {IMovie, Movie} from '../movie';
import {Router} from 'angular2/router';
import {MovieService} from '../movies.service';

@Component({
    templateUrl: 'app/movies/create/movie-create.component.html'
})

export class MovieCreateComponent implements OnInit {
    title: string = 'Create New Movie';
    newMovie: IMovie;

    constructor(
        private _moviesService: MovieService,
        private _router: Router
    ) { }

    ngOnInit() {
        this.newMovie = new Movie;
    }

    createMovie(event) {
        let _this = this;

        this._moviesService.createMovie(this.newMovie)
            .subscribe(function () {
                _this._router.navigate(['Movies']);
            });
    }

    cancelCreate(event) {
        this._router.navigate(['Movies']);
    }

}angular2_form.PNG
Router:
route.PNG

In the index.html page, put the baseurl:
baseUrl.png

Done, have fun!

The architecture of real live AngularJS — Get start with angularJs — Part 2

A good project should has a good architecture, and in angularjs is very easy to modular your projects. And also your project will be easy to extend it.

For example like this:

(function () {
    "use strict";
   
   // app.js
    var app = angular.module("productManagement",
                            ["services",
                             "productResource"]);
}());

//this is services.js
(function () {
    "use strict";
    angular
        .module("services",
                ["$http"])
}());
 

//this is my productResource.js
(function () {
    "use strict";
    angular
        .module("services")
        .factory("productResource",
                ["$http",
                 productResource]);

    function productResource($http) {
        //url could be external or internal
        return $http("/api/products/:productId")
    }
}());

So every javascript file just has few lines. And it separate the angular project to three modules, the main moudule is the “productManagement” and it has two dependants are “services” and “productResource”.

I came from C# background, so I like write code also follow the rule  single responsibility and dependency inversion.

It is much easy to read and extensible.

Basic struction — create Database first ASP MVC Application(Models in three way)

Here in the internet have a lot tutorial introduction about create code first asp mvc application, but in the real life world the database always aready is exsiting. I am not mvc experter, I am fresher now. It took me a while to get the basic struction a while.So I like to write it down wish this could help some people like me struggling in the beginning.

OK, let me asume that we are going to create a online store ecormmerce website.

Way one: basic struction

Then you create a new MVC project through VS2013, first thing is add a connection string to your root web.config file.

It is like this:connectionStr

Modelsisnothingjustclassfile

Models is nothing just class file. You map your table to class file, then when you create a controller you could select strongly type.

finally is the Add one line to Global.asax to stop populater some seed data

GlobalNeedAddOneLine

See the null meaning don’t popular seed data.

Way two: basic struction(Models is another project)

I won’t talk about the controller or the views. But Models could anything, Web services, WCF and don’t sit in your project, the model is not neccessary sit in your project.So your project only have controller and Views, but the Models is independent. Here is a simple practise I did in my Demo struction is like this:

separateModelMvc

Way three: basic struction(Model is your entity Framework)

MvcEntityFrameworkStruction

Revision:how to use backbone

three way to create your  model:

Person = Backbone.Model.extend({})
Person = Backbone.Model.extend({
defaults:{
name:"";//Model arrow undefined data
create:new Date() //here arrow put PHP function
}
});
Person = Backbone.Model.extend({
defaults:function(){
return   {  name:"";  //Model arrow undefined data
create:new Date()
}
}
});

Read and Write:

var person = new Person({ name:”Sandy”,age:”12″});

person.get(“name”);//” Sandy”

person.set({name:”Anna”,age:”12″});//overwrite it

Create Simple View

PersonView = Backbone.View.extend({

render:function(){

var person = “<p>” + this.model.get(“user”) + “</p>”;

$(this.el).html(person);

}

});

“this.el” meaning: every View Model create a Page Scope, el means the root tag<div></div>,

Get Model data Add into HTML page

var personView = new PersonView({

model:person;

})

personView.render();

console.log(personView.el);//check the el

Get Data from the server(RESTful way)

var Person = Backbone.Model.extend({urlRoot:”/person”}); //point to the data sourse

var person = new Person({id:1}); //point to your object

person.fetch();//get /person/1

person.save();//put /person/1

var newPerson=new PersonModel(); //create a new object

newPerson.set({name:”Sandy Zhang”}); // give the an name

newPerson.save(); // insert into /person

newPerson.get(“id”); //2

newPerson.destroy();// Delete /person/2

Create Better View

var PersonView = Backbone.View.extend({

tagName:”article”,

id:”personOne”;

class:”person”,

attributes:{title:this.model.get(“name”)},

render:function(){

var person=”<p>” + this.model.get(“name”)+”</p>”;

$(this.el).html(person);

}

});

this will render html sourse:

<article id=”personOne” class = “person” title=”Sandy Zhang”>

<p>Sandy Zhang</p>

</article>

Cache of the jQuery object

$(“#personOne”).html();

in Backbone.js: $(this.el).html(); or better way : this.$el.html()

el  is  generated for the cache of jQuery object, so that it can be used repeatedly without fear of generating multiple instances of the jQuery object.

Built-in template engine(underscore template)

use render() seems not elegant ,now we use underscore template

var PersonView = Backbone.View.extend({

tagName:”article”,

id:”personOne”;

class:”person”,

attributes:{title:this.model.get(“name”)},

template: _.template(‘<p><%= name %></p>’),

render:function(){

data = this.model.toJSON();

$(this.el).html(template(data));

}

}); // Cool! looking better

Track your instance

var person = new Person({name:”sandy”}); //create a instance

person.on(“change”,function(){

alert(“Something changed on ” + this.get(“name”) );

});

person.on(“change:age”,function(){

alert(this.get(“name”) +”,Your age has been changed ”  );

});

Change data to JSON format

var person = new Person({id:1});

console.log(person.toJSON());

//or

console.log(JSON.stringify(person));

Now Add View event

var PersonView = Backbone.View.extend({

tagName:”article”,

id:”personOne”;

class:”person”,

attributes:{title:this.model.get(“name”)},

template: _.template(‘<p><%= name %></p>’),

render:function(){

data = this.model.toJSON();

this.$el.html(template(data));

},

events:{ “mouseover” :”showMore”,},

showMore:function(){…….},

});

Advance Level

1.Server <= (get) model (data) => view (rendering) => DOM

var PersonView = Backbone.View.extend({

input:_.template(‘<input type=”text”><%= name%></input>’),

submit: _.template(‘<button type=”submit”>Change Name</button>’),

render: function(){

data = this.model.toJSON();

this.$el.html(input(data)).html(submit);

}

});

Now we can  change name now, the question is how do you know the model data has changed? This needs to view it through the event.

var PersonView = Backbone.View.extend({

events:{“submit button”:”updateName”}

updataName:function(){

newName = $(this).val();  // this=button!

if(newName !== this.model.get(‘name’)){

this.model.set({name:newName}); }

else{ return;}

}

});

Well…it did work, but the problem is : should belong Model processing logic  put in the View now. So we reconstruct it.

var PersonView = Backbone.View.extend({

events:{“submit button”:”updateName”}

updateName:function(){

newName = $(this).val();

this.model.updateName(newName);

}

});

var Person = Backbone.Model.extend({

updateName: function(newName){

this.set({name:newName});

this.save(); // tell server to save the new name into database

}

})

2.The above, we pass the parameter value changes transferred to the Model, and then further processed by the Model it wants to do.

Now, the loop model like this:

Server <= (updated) model (proceed) <= (notification) view <= (user interaction) DOM

If the data model changes, how to notice the view? You might immediately think of the render method can be performed immediately after the completion notification view:

var PersonView = Backbone.View.extend({

updateName:function(){

newName = $(this).val();

this.model.updateName(newName);

this.render();

}

});

Ah good idea …… but it is not always useful, because the value of the model could be changed elsewhere, such as in another view . So, how to notify the specified view  response to the model changes?

So, use listenTo() function

var PersonView = Backbone.View.extend({

initialize:function(){

this.listenTo(this.model,”change”,this.render);

//this.model.on(“change”,this.render,this);

}

});

3. collection

a)Collect multiple model data

When there was more and more instances , in order to facilitate the processing of them, we usually think of them together with the array. Backbone.js Collection module provides a simplified way to help us:

var Person = Backbone.Collection.extend({ model:person });

var person.add{[{name:”Sandy Zhang”, id:1},{name:”Anna li”,id:2}]};

person.length //=>2

person.get(2) // => { name : “Anna li”,id:2}

person.at(0) //=>{ name:”Sandy Zhang”, id:1}

HTML5 Push State API

Backbone.history.start({pushState: true});

var App = new (Backbone.Router.extend({
initialize: function() {
this.person = new person();
this.personView = new personView({collection: this.person});
$(“#app”).append(this.personView.el);
},

index: function() {
this.person.fetch();
},

start: function() {
Backbone.history.start({pushState: true});
}
}));

$(function() { App.start(); });

Backbone Collection

run the code in the browse should look like

backbone3

here is the code for get the collection:

<script type=”text/javascript”>
Student = Backbone.Model.extend({
defaults:{
name:””,
age:””
}

});
ClassRoom = Backbone.Collection.extend({
model:Student
});

var student1 = new Student({name:”Sandy”,age:”20″});
var student2 = new Student({name:”Rachel”,age:”18″});
var student3 = new Student({name:”Anna”,age:”23″});
var collection = new ClassRoom([student1,student2,student3]);

</script>

Backbone – Router

On this post create 2 template,and use backbone router to communicate with those 2 pages, here is the code.

<script type=”text/template” id=”first_template”>
<label>My First View:</label>
<a href=”#second”>Go to Second View</a>

</script>

<script type=”text/template” id=”second_template”>
<label>My Second View:</label>
<a href=”#first”>Go to First View</a>

</script>

<div id = “container”></div>

<script type=”text/javascript”>
FirstView = Backbone.View.extend({
el:$(‘#container’),
initialize:function(){
this.render();
},

render:function(){
var template = _.template($(‘#first_template’).html(),{});

this.$el.html(template);

}

});

SecondView = Backbone.View.extend({
el:$(‘#container’),
initialize:function(){
this.render();
},

render:function(){
var template = _.template($(‘#second_template’).html(),{});

this.$el.html(template);

}

});

MyRouter = Backbone.Router.extend({
routes:{
“”:”firstPage”,
“first”:”firstPage”,
“second”:”secondPage”
},

firstPage:function(){
new FirstView();
},
secondPage:function(){
new SecondView();
}

});

var router = new MyRouter();
Backbone.history.start();

</script>

 

Backbone create a View

On this post just create a event and render it, and the event communicate with your html ID.

here is the code for your view, just put this in your body will work.

test on the browser should look like this.

backbone2

<script type=”text/template” id=”sample_template”>
<label>Name:</label>
<input type=”text” id=”name_desc”/>
<input type=”button” value=”Add Name” id=”btn”/>

</script>
<div id = “container”></div>
<script type=”text/javascript”>
Name = Backbone.View.extend({
el:$(‘#container’),
initialize:function(){
this.render();
},

render:function(){
var template = _.template($(‘#sample_template’).html(),{});

this.$el.html(template);

},
events:{
“click input[type=button]”: “addName”
},

addName:function(){
alert(“The name: “+$(‘#name_desc’).val() + ”  has been added”);
}
});

var name = new Name();
</script>

Backbone setup your Model

This could put on external file or you could put in the body area, test in browser, you should see things like this

backbone1

<script type=”text/javascript”>
           //create a new model named person, if you work with PHP framework like zend framework just use the model name you create in your php files
Person = Backbone.Model.extend({
initialize: function(){
console.log(“Object is created”);

//this is for track the value weather it changed or not
this.on(“change:name”,function(){
alert(“Name attribute value has been changed”);
});
this.on(“change:age”,function(){
alert(“Age attribute value has been changed”);
});
},
defaults:{
name:”Sandy”,
gender:”Female”,
age:”20″
},
//create a customer function
getPersonInfo:function(){
document.write(“<h2>Name is: ” +this.get(‘name’)+”,Gender is : “+this.get(‘gender’)+”,Age is:”+this.get(‘age’) +”</h2>”);
}
});
//create an object name sandy
var sandy = new Person();
document.write(sandy.get(‘name’)+”<br/>”);
document.write(sandy.get(‘gender’)+”<br/>”);
document.write(sandy.get(‘age’)+”<br/>”);
//create an object name simon and give different value to name,gender, age
var simon = new Person({name:”simon”,gender:”male”,age:”12″});
document.write(simon.get(‘name’)+”<br/>”);
document.write(simon.get(‘gender’)+”<br/>”);
document.write(simon.get(‘age’));
//call the customer function
sandy.getPersonInfo();
//this set function goes to overide the name then will have alert window
sandy.set({name:”SandyZhang”});
sandy.set({age:”30″});
</script>

Set up your backbone

Set up your backbone is really ease, just down the backbone, and underscore into your local file, and the jQuery just use the google jQuery, so you just add this the link to your web page, it will fast your website(this is called CDNS: content delivery networks,common quesion for fornt-end designer interviews).

so your page’s code just like this:

<!DOCTYPE HTML>
<html>
<head>
<title>Backbone Tutorials</title>
<script src=”http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js”></script&gt;
<script src=”underscore.js” type=”text/javascript”></script>
<script src=”backbone.js” type=”text/javascript”></script>
</head>
<body>
<script type=”text/javascript”>

</script>

</body>

</html>