Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 1_ApplicationFoundations.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ It should look like this:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>
<script src="libs/can.custom.js"></script>
<script src="//canjs.com/release/2.1.4/can.fixture.js"></script>
<script src="app.js"></script>
<script src="app.js"></script>
</body>
</html>

Expand Down
92 changes: 32 additions & 60 deletions 2_FirstComponent.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,62 +14,16 @@

- - -

##Constructors in CanJS

Before we work with any of the objects in CanJS, it will be helpful for us if we understand can.Construct. Most of the objects in CanJS are derived from can.Construct. can.Construct provides a way to easily use the power of prototypal inheritance without worrying about hooking up all the particulars yourself.

Without going into exhaustive detail[^ConstructDetail], can.Construct contains a few methods we'll encounter frequently in other objects:

[^ConstructDetail]: If you want to go into exhaustive detail on can.Construct, you can consult the [CanJS docs](http://canjs.com/docs/can.Construct.prototype.init.html).

- Prototype
- init
- Static
- extend

We'll look at the extend method first.

###The extend method
can.Construct's `extend` method is used to create "constructor functions". Constructor functions create instances of objects. The extend method can take up to three arguments:

1. name: string
2. staticProperties: object
3. instanceProperties: object

The extend method behaves differently depending on the number of arguments you pass it. The name and staticProperties arugments are optional. For example, if you pass it one argument, it will be use the value you pass it to set its instanceProperties. If you pass it two arguments, it use the first to set its staticProperties, and the second to set its instanceProperties. Finally, if you pass in all three arguments, the first will set its name, the second in staticProperties, and the third its instanceProperties.

In the example below, I only want to pass in staticProperties. Therefore, I must call the method as follows:

can.Construct.extend({
//Static properties here
},
//Blank object as second parameter
{});

This pattern will apply to all objects in CanJS that have an extend method.

###The init method
The `init` method is called whenever a new instance of a can.Construct is created. Init is where the bulk of your initialization code should go. Inside of the init function, the `this` keyword will refer to the new instance, and `this` will contains the arguments passed to the constructor. A common thing to do in init is save the arguments passed into the constructor. An example is below:

var Person = can.Construct.extend({
init: function(first, last) {
this.first = first;
this.last = last;
}
});

var actor = new Person("Abe", "Vigoda");

##First can.Component <a name="first-component"></a>
If you recall from the introduction, a can.Component is like a self-contained, mini web application---i.e., it's encapsulated. Because can.Components are encapsulated, they should each contain their own:

- View template (.stache file)
- JS
- CSS

This is why we created a components folder for our app---instead of, say, a JS folder. Each component we develop will be in a folder that contains all the files that support that component. This makes components portable, enabling you to reuse them across projects. It also isolates them, making them easier to test and maintain.
This is why we created a components directory for our app---instead of, say, a JS directory. Each component we develop will be in a directory that contains all the files that support that component. This makes components portable, enabling you to reuse them across projects. It also isolates them, making them easier to test and maintain.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some strange reason, "folder structure" threw me off. "directory structure" sounds more correct to me, but that's completely subjective.


In the components folder, create a subfolder called "restaurant_list". Inside that, create the following files:
In the components directory, create a subfolder called "restaurant_list". Inside that, create the following files:

- restaurant_list_component.js
- restaurant_list.stache
Expand All @@ -91,7 +45,7 @@ Put the following code inside restaurant_list_component.js:

Add the following code to restaurant_list.stache:

<div>{{currentRestaurant}}</div>
<h1>{{currentRestaurant}}</h1>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish this example wasn't made irrelevant by the next example.


Add the code below to the /app/base_template.stache file:

Expand Down Expand Up @@ -119,11 +73,15 @@ Finally, we need to add a reference to restaurant_list_component.js in the index

Now, go back out to your app in the browser, and refresh it. You should see it printing: "Hello Restaurant Customer".

#### Component tag names

A component's tag name must contain a dash (-). So for example, `<x-tags>`, `<my-element>`, and `<my-awesome-app>` are all valid names, while `<tabs>` and `<foo_bar>` are not.

###Auto Instantiation

If you recall from the discussion above regarding can.Construct, whenever you declare an object using can.Construct it must be instantiated. Normally, you would either directly instantiate objects using the `new` keyword, or pass the constructor to an object that would create instances of it. *can.Component is an exception*.
Typically in an object oriented programming environment you instantiate objects using the `new` keyword, or pass the constructor to an object that would create instances of it. *can.Component is an exception*.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's necessary to introduce can.Construct at all.


All we have to do is declare the can.Component using its `extend` method. Once you declare your can.Component, you've registered your component with the system. When CanJS parses the base_template.stache file, and encounters the restaurant-list tag, it will automatically instantiate the can.Component associated with it, generate the Component's view inside of its custom tag, and bind that view to your component's scope.
All we have to do is declare the can.Component using its `extend` method. By extending can.Component, you've registered your component with the system. When CanJS parses the base_template.stache file, and encounters the "restaurant-list" tag, it will automatically instantiate the can.Component associated with it, generate the Component's view inside of its custom tag, and bind that view to your component's scope.

Let's look at an image that describes how all of this works, to make it clearer:

Expand All @@ -146,24 +104,38 @@ As mentioned above, when the template containing the can.Component's tag is pars
![](images/2_first_component/ComponentTagRenderedHTML.png)

####Template
The `template` property of the can.Component contains the string value of the can.Component's template. Note that the template property just contains a string value. You can inline the template, if it is small. However, the recommended way of working with templates, to maintain separation of concerns, is to keep them in their own files and load them using can.view, as we have done here.
The `template` property of the `can.Component` contains the string value of the `can.Component`'s template. Note that the template property just contains a string value. You can inline the template, if it is small. However, the recommended way of working with templates, to maintain separation of concerns, is to keep them in their own files and load them using `can.view`, as we have done here.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like to format code strings. Not sure how that works with DocumentJS though.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure myself. I think what I was trying to say is that DocumentJS probably has a link/format for these things already.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean a format for abc? Yes DocumentJS has a style for that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same syntax?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it uses the same markdown syntax that github supports.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. I'll keep formatting them then.


####Scope
The `scope` object is the can.Component's view model. The view model is an abstraction of the view that exposes public properties and functions. Any property or method defined on the scope object is available from the can.Component's template as either a Stache data key, or a function. In our example above, we created a property, "currentRestaurant", and then referenced it as a Stache data key in our template.
The `scope` object is the can.Component's view model. The view model is an abstraction of the view that exposes public properties and functions. Any property or method defined on the scope object is available from the can.Component's template as either a Stache data key, or a function. In our example above, we created the property "currentRestaurant", then referenced it as a Stache data key in our template.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed a few commas here.


![](images/2_first_component/ComponentScopeTemplateLink.png)

#####can.Map &amp; can.List
The scope is a special type of object, called a "can.Map". can.Map objects are observable. Observable objects provide a way for you to listen for and keep track of changes to them. What this means, in this instance, is that if you make a change to your scope, those changes will be reflected automatically in your view. If you've cross-bound the values between your scope and your view, changes to your view will also be reflected in your scope. We'll see how this works in the next chapter.
The scope is a special type of object, called a "can.Map". can.Map objects are observable. Observable objects emit events whenever their properties change. can.view (and subsequently can.Component) subscribe to these events and update the DOM as needed. If you've cross-bound the values between your scope and your view, changes to your view will also be reflected in your scope. We'll see how this works in the next chapter.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if going into can.Map any further than this is unnecessary at this point.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe it should be moved into it's own section? I think going into can.Map and can.List is way more important than can.Construct.

can.Map is able to emit these events because of the `attr` method. This is important. In order to dispatch the associated events when a property is changed on a `can.Map`, you must use the `attr` method when setting or getting a value.

The `attr` method can be used to either get or set a property on a can.Map.

var myCanMapInstance = new can.Map({
id: 12,
person: {
name: {
last: 'Ludwig'
last: 'Beethoven'
}
}
});
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I felt like going into "dot" notation/deep properties was a bit of a leap from the first paragraph, so I added this example. But now both of these examples seem out of place.


can.Map objects listen for changes made using their `attr` method. This is important. In order to broadcast the associated events when you change a property on a can.Map, you must use the attr method when setting a value.
myCanMapInstance.attr('id'); // -> 12

The attr method can be used to either get or set a property on a can.Map. `attr` works with deep properties---i.e., properties within properties. Here's an example:
`attr` works with deep properties---i.e., properties within properties. Here's an example:

//Get the first property off of the name property off of person
myCanMapInstance.attr('person.name.first');
//Get the first property off of the name property off of person
myCanMapInstance.attr('person.name.first');

//Set the last property of the person's name property
myCanMapInstance.attr('person.name.last', 'Bach');
//Set the last property of the person's name property
myCanMapInstance.attr('person.name.last', 'Bach');

Observable arrays are also available with can.List, which is based on can.Map.
6 changes: 3 additions & 3 deletions 3_FirstComponentContinued.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ Next, you'll notice that when you select a restaurant from the list, the followi

![](images/3_first_continued/RestaurantDetailsFirstDisplay.png)

We set up the display of the current restaurant section earlier in the template. The default value for currentRestaurant, when the RestaurantListComponent is first loaded is 'undefined'. Setting the value to 'undefined' causes the Stache template to remove it from the DOM. As soon as we set currentRestaurant to a valid value, the scope, which is an observable can.Map, broadcasts this change, and the template refreshes automatically, rendering the current restaurant section.
We set up the display of the current restaurant section earlier in the template. The default value for `currentRestaurant`, when the `RestaurantListComponent` is first loaded is `undefined`. Setting the value to `undefined` causes the view to remove it from the DOM. By changing the `currentRestaurant` of the scope (which is an observable `can.Map`) to a valid value a "change" event is triggered. Having known that the template depends on this value, the view is listening for this change event and makes the appropriate changes to the DOM based on the new value and the template.

##View Models
It's considered a best practice to keep your can.Components thin. This helps maintain readability, and maintainability. To accomplish, you extract your scope from the can.Component into a can.Map.
## View Models
It's considered a best practice to keep your can.Components thin. This helps maintain readability, and maintainability. To accomplish this, extract your scope from the can.Component into a can.Map.

Open up restaurant_list_component.js, and add the following code:

Expand Down
29 changes: 23 additions & 6 deletions 4_Models.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

- - -

The next item we're going to go over is can.Model. Models make interacting with JSON REST services *really easy*. They do this by encapsulating most of the code required to connect to a service, and managing the data the service returns. Additionally, can.Model extends can.Map, meaning that the objects returned have all of the features of a can.Map, such as being observable.
The next item we're going to go over is can.Model. Models make interacting with JSON REST services *really easy*. They do this by abstracting most of the code required to connect to a service, and managing the data the service returns. Additionally, can.Model extends can.Map, meaning that the objects returned have all of the features of a can.Map, such as being observable.

We'll use a can.Model to provide data for our restaurant list.

Expand All @@ -22,11 +22,11 @@ In the models folder, create a file called "site_models.js". Add the following c
var RestaurantModel = can.Model.extend({
findAll: "GET /restaurants"
},
//Include second, blank parameter object to set staticProperties
//Include second, blank parameter object to set instanceProperties
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo.

{});


Because it is a can.Construct, can.Model.extend can take up to three parameters:
Like most constructors in CanJS, can.Model.extend can take up to three parameters:
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, trying to avoid referring to can.Construct.


1. name
2. staticProperties
Expand All @@ -40,13 +40,30 @@ A can.Model's staticProperties parameter has several reserved properties you can
4. update
5. destroy

The findXxx methods are available directly off of the object definition (i.e., they are static). The create, update, and destroy methods are available off of specific instances of a can.Model. We'll see how to use these below.
The findXxx methods are properties of the contructor (i.e., they are static). The `create`, `update`, and `destroy` methods are properties of the prototype. So for example..

```
var MyModel = can.Model.extend({
findAll: function () {
// Static method
}
}, {
destroy: function () {
// Instance method
}
});

MyModel.findAll(); // Reference a method defined on the contructor

var modelInstance = new MyModel();
modelInstance.destroy(); // Reference a method defined on the prototype
```
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe unnecessary, but I thought I'd try it out for size.


**Reminder**: The number of parameters you pass in to an extend method is important. If you pass in a single parameter object, the extend method will use that to set the instanceProperties. If you pass in two parameter objects, the *first* object passed in will be used to set the *staticProperties*. The second parameter will be used to set the *instanceProperties*. Here, we only want to set the staticProperties, so we must pass in a second, blank object.

##The Data for Our Model

We're not going to connect to a server to retrieve our data; however, we're going code our model as if we were. How can this possibly work? CanJS provides a handy utility, can.fixture, that we can use to mimic the functionality of connecting to a server. As the CanJS docs say, "can.fixture intercepts an AJAX request and simulates the response with a file or a function. You can [can.fixutres] to develop JavaScript independently of backend services."
We're not going to connect to a server to retrieve our data; however, we're going to code our model as if we were. How can this possibly work? CanJS provides a handy utility, can.fixture, that we can use to mimic the functionality of connecting to a server. As the CanJS docs say, "can.fixture intercepts an AJAX request and simulates the response with a file or a function. You can use [can.fixture] to develop JavaScript independently of backend services."

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typos.

can.fixture is not included with the base CanJS package. It's a good practice to keep it separate from your production CanJS library, which is why we downloaded it from its CDN in a separate script tag, rather than including it with our custom download. *If you use can.fixture during development, remember to remove it once you are connecting to your REST services*.

Expand Down Expand Up @@ -81,7 +98,7 @@ Let's create a fixture that will respond to our requests for menu item data. Cre
];
});

The first argument to can.fixture, "GET /restaurants", tells CanJS to intercept any GET requests to the resource "/restaurants". The second argument is a function that returns the data we want to get when the application makes a service call. Because we're simulating a findAll method, we need to return an array. The findAll method expects an array. By default, if it does not receive one, it will throw an error. If you need to connect to services that return data that doesn't match the expected return type of the findXxx methods, don't fret. There are ways to manage this, which we'll work with later on.
The first argument to can.fixture, "GET /restaurants", tells CanJS to intercept any GET requests to the resource "/restaurants". The second argument is a function that returns the data we want to get when the application makes a service call. Because we're simulating a findAll method, we need to return an array. The findAll method expects an array. By default, if it does not receive one, it will throw an error. If you need to connect to services that return data that doesn't match the expected return type of the findXxx methods, don't fret. There are ways to manage this, which we'll learn later on.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- work with later on. 
+ learn later on. 


##Connecting the Model to the Component

Expand Down
Loading