Coder Social home page Coder Social logo

hello-wire.js's Introduction

Hello wire.js

This is a simple Hello World example for wire.js, a Javascript IOC Container.

Running the demo

To run the demo:

  1. git clone https://github.com/briancavalier/hello-wire.js
  2. cd hello-wire.js
  3. npm start
  4. open localhost:8000 in your browser

Simple and Ridiculous Example - Hello wire()d!

Here's a very simple wire.js take on Hello World. Wire.js can use AMD modules, so first, let's use AMD to define a very simple wiring spec. Wiring specs are simply JSON or Javascript objects.

define('hello-wired-spec', [], { message: "Hello wire()d!" });

In this case our wiring spec is a laughably simple object with a single String property. Next, let's wire() the spec using wire.js as an AMD plugin.

require(['wire!hello-wired-spec'], function(wired) {
	alert(wired.message);
});

As you probably guessed, this will alert with the message "Hello wire()d!". Yes, that was silly, so let's create a more interesting Hello wire()d.

Simple, but Less Ridiculous Hello wire()d!

You can run this example by cloning the repo and loading index.html in your browser. First, we'll define a component using AMD:

define([], function() {
	function HelloWired(node) {
		this._node = node;
	}

	HelloWired.prototype = {
		sayHello: function(message) {
			this._node.innerHTML = "Hello! " + message;
		}
	};

	return HelloWired;
});

This is a simple module that returns a Javascript constructor for our HelloWorld object. Now, let's create a wiring spec for our tiny Hello wire()d app:

define({
	// A regular String.  Basic Javascript types are supported directly
	// in wiring specs, even non-primitives like Date and RegExp.
	message: "I haz been wired",

	// Create an instance of the hello-wired module.
	helloWired: {

		// The hello-world module returns a constructor function, which
		// wire.js will call to create the instance, passing a single
		// parameter, the first DOM Node with the CSS class "hello".  This uses
		// JSON Reference syntax along with the `dom.first!` resolver provided
		// by the `wire/dom` plugin below.
		create: {
			module: 'app/hello-wired',
			args: { $ref: 'dom.first!hello' }
		},

		// Invoke the sayHello method on the instance after it is
		// created, and pass a single parameter, the message String
		// defined above.  Again, you can use JSON Reference syntax to
		// reference other objects in the wiring spec.
		init: {
			sayHello: { $ref: 'message' }
		}
	},

	plugins: [
		// The debug plugin outputs wiring progress and diagnostic info
		// to the console
		{ module: 'wire/debug' },
		// Load the basic wire.js dom plugin, which provides the `dom!`
		// resolver used above.
		{ module: 'wire/dom' }
	]
});

And finally, let's create a page for our little app

<!DOCTYPE HTML>
<html>
<head>
    <title>Hello wire()d!</title>

    <script src="lib/curl/src/curl.js"></script>
    <script src="app/run.js"></script>
</head>

<body>
    <header>
        <!-- Message will go here -->
        <h1 class="hello"></h1>
    </header>
</body>
</html>

When you load this page in a browser, you'll see the text "Hello! I haz been wired" in the h1.

What Happened?

The Page

So, what happened when we loaded the page? Let's start with two interesting parts of the page. First, there is a script tag to load curl.js, an AMD loader--wire.js uses the loader to load AMD style modules.

Immediately after that, there is another script tag that loads app/run.js which configures the loader and sets everything in motion.

<!-- AMD Loader, in this case curl.js -->
<script src="lib/curl/src/curl.js"></script>
<script src="app/run.js"></script>

run.js - The boostrap

The run.js script simply sets up the AMD loader config, and then calls the loader. Wire.js can be used as either an AMD loader plugin or as an AMD module. In this case, we're using it as an AMD plugin. In particular, we're using wire.js to load and process the wiring spec defined in the AMD module whose id is app/main.

var config = {
    packages: [
        { name: 'curl', location: 'lib/curl/src/curl', main: 'curl' },
        { name: 'wire', location: 'lib/wire', main: 'wire' },
        { name: 'when', location: 'lib/when', main: 'when' },
        { name: 'meld', location: 'lib/meld', main: 'meld' },
        { name: 'poly', location: 'lib/poly', main: 'poly' }
    ]
};

curl(config, ['wire!app/main']);

main.js - The wiring spec

Now let's walk through the wiring spec to see what happens when wire.js processes it. First, there is the standard AMD module define wrapper:

define({
...
});

Next we have a String property. This does what you probably expect. It creates a String property named message whose value is "I haz been wired".

// A regular String.  Basic Javascript types are supported directly
// in wiring specs, even non-primitives like Date and RegExp.
message: "I haz been wired",

Then we have the helloWired property, which is more interesting:

// Create an instance of the hello-wired module.
helloWired: {

	// The hello-world module returns a constructor function, which
	// wire.js will call to create the instance, passing a single
	// parameter, the first DOM Node with the CSS class "hello".  This uses
	// JSON Reference syntax along with the `dom!` resolver provided
	// by the `wire/dom` plugin below.
	create: {
		module: 'hello-wired',
		args: { $ref: 'dom.first!.hello' }
	},
	
	// Invoke the sayHello method on the instance after it is
	// created, and pass a single parameter, the message String
	// defined above.  Again, you can use JSON Reference syntax to
	// reference other objects in the wiring spec.
	init: {
		sayHello: { $ref: 'message' }
	}
},

This creates an instance of the hello-wired module, invoking it as a constructor, and passing a single argument (we'll see what that argument is below, but you can probably guess). Then wire.js will invoke the sayHello method on the hello-wired instance, again passing a single argument.

So, this part of the spec creates an instance of hello-wired, and initializes it by calling its sayHello method.

Finally, in the wiring spec, we have an Array named plugins. There is nothing special about the name plugins--it is not a wire.js keyword (wire.js has very few keywords, and most functionality is provided via pluggable syntax from plugins).

plugins: [
	// The debug plugin outputs wiring progress and diagnostic info
	// to the console
	{ module: 'wire/debug' },
	// Load the basic wire.js dom plugin, which provides the `dom.first!`
	// resolver used above.
	{ module: 'wire/dom' }
]

The array has a single element, which is an object. That object does use one of wire.js's keywords, module. In this case, wire.js will load the AMD module wire/dom. So the result is an array named plugins with a single element whose value is the result of loading the module wire/dom.

That AMD module just happens to be a wire.js plugin. Plugins can provide several types of features, but in this case, the wire/dom plugin provides several reference resolvers for DOM nodes. Reference resolvers provide a way to resolve references to other things, like other objects in the wiring spec, or, in this case, DOM nodes on the page, and it does so without your having to worry about DOMReady.

Ok, Now Back to Those Arguments

You probably guessed that there is some relationship between the wire/dom plugin and the { $ref: 'dom.first!.hello' } bit in the helloWired object. Yep, there is. When instantiating the helloWired object, wire.js will pass its constructor a single parameter (it is also possible to pass multiple parameters using an array, but let's keep it simple for now):

	args: { $ref: 'dom.first!.hello' }

That single parameter is a reference to a DOM Node whose class="hello". This is Dependency Injection at work. The hello-wired instance needs a DOM Node to do it's job, and we have supplied one by referencing it using the wire/dom plugin's dom.first! reference resolver.

You can think of this as:

	new HelloWired(document.querySelector(".hello"))

but you'd also need to add your own DOMReady wrapper/check, which wire.js gives you for free.

Then, when wire.js invokes the sayHello method on the instance, it also passes a single parameter:

	sayHello: { $ref: 'message' }

In this case, the parameter is a reference to the message String, which is the first item in the spec. You can think of this as:

	helloWired.sayHello(message)

Parameters don't have to be references. For example, we could have just as easily provided a message inline:

	sayHello: "I haz been wired"

Finally, a Note on Order

Wiring specs are declarative, and thus order of things in a wiring spec doesn't matter. For example, in this example, the wire/dom plugin was declared after the { $ref: 'dom.first!.hello' } reference. That's no problem. Wire.js ensures that the plugin is ready before resolving the DOM Node reference.

Also notice that we didn't have to write any code to wait for DOM Ready. Again, wire.js ensures that the DOM Node reference is resolved only after the DOM is indeed ready.

As you can imagine, there is an implicit ordering to the things that happen, even in this simple Hello Wire example. The wire/dom plugin must be loaded and ready before the DOM Node reference can be resolved, which must happen before the HelloWired instance can be created, since it requires the DOM Node as a constructor parameter. And finally, the sayHello initializer method can only be invoked after the HelloWired instance has been created.

This concept of automatic ordering is a key feature of wire.js. You simply write a declarative wiring spec, that is, you describe what you want, and wire.js makes it happen, without your having to worry about the order.

Hello Wire is a trivial example, and the code to do this ordering yourself would be trivial, but as you deal with larger and larger systems with more and more collaborating components, it can be a big advantage to let wire.js take care of these kinds of ordering issues.

hello-wire.js's People

Contributors

briancavalier avatar jaredcacurak avatar rajatarun avatar

Stargazers

Cat  avatar Ernst Salzmann avatar Andreas Schaeffer avatar  avatar Branden Pinney avatar  avatar Denis Tokarev avatar Fintan avatar Geert avatar Brad Pillow avatar  avatar Tim Spann avatar Konstantin Shutkin avatar Rio Purnomo avatar mparaiso avatar André Cruz avatar uly6 avatar Joe Esposito avatar Jason Cline avatar Sönke Nommensen avatar Tom Elam avatar Michael Alan Dorman avatar Mara Morton avatar Dave McCrory avatar Mike Wagner avatar  avatar Nuba Princigalli avatar  avatar Christian Högl avatar Kevin Isom avatar  avatar

Watchers

 avatar Tom Elam avatar  avatar James Cloos avatar  avatar

hello-wire.js's Issues

Backbone's prepareModel breaks with Collections at prepareModel line 575

I have a collection of items, and in certain scenarios, when I try to add objects to my collection, i get an error at prepareModel, line 575 of Backbone.js

_prepareModel: function(model, options) {
if (!(model instanceof Backbone.Model)) {
var attrs = model;
model = new this.model(attrs, {collection: this});
if (model.validate && !model._performValidation(model.attributes, options)) model = false;
} else if (!model.collection) {
model.collection = this;
}
return model;
},

model = new this.model(attrs, {collection: this});

TypeError: object is not a function

my model

model:
Object
code: "ROKU"
description: "Roku Streaming Players"
gridSortOrder: "0"
image: "http://qa.hbogo.com/asset/images/devices/activate-roku.png"
proto: Object

this is...

Begetter
_byCid: Object
_byId: Object
_callbacks: Object
_onModelEvent: function () { [native code] }
_removeReference: function () { [native code] }
length: 0
model: Device
models: Array[0]
select: function (){ return fn.apply(me, arguments); }
selectByDescription: function (){ return fn.apply(me, arguments); }
url: "/apps/profi

I am passing the deviceCollection is a constructor argument, but inside Backbone's collection, he's expecting an instance, not a begetter, here's the line from my context File for that view.

    deviceCollection: {
        create: {
            module: 'models/deviceCollection'
        },
        properties: {
            url: "/apps/profile/rest/device/list.json?deviceCode=DESKTOP&filter=ACTIVATION"
        }
    },

    deviceView: {
        create: {
            module: 'views/deviceView',
            args: [{model:{$ref:'deviceCollection'}}]
        }
    },

Thanks for your help, this is really challenging.

When using r.js to build a wire project - context File doesn't load on built file.

activate.js:14026globals initialized
log4js.js:1794activation~ 16:18:35:607 [FATAL] Error in (http://beta.inhbogo.com/activate/js/libs/requirejs_107/require.js) on line 1633 with message (Uncaught Error: Load timeout for modules: wire!specs/applicationContext
http://requirejs.org/docs/errors.html#timeout)
require.js:1633Uncaught Error: Load timeout for modules: wire!specs/applicationContext
http://requirejs.org/docs/errors.html#timeout

Not really sure why we get this error, we closely build our project like Peter's although the project builds, our context properties don't seem to be injected. Also, we notice that this project complains about context not loading when the project loads fine in development mode.

require.js version

It be great to get a require.js version.
If you don't I will when I find time.

Problem in bin/server calling express when "npm start"

Hi,

I was trying to follow the demo instructions, and when I did "npm start", the bin/server script fails that way :
hello-wire-bug-express

It seems there is a problem with express script inside Node, but I'm a novice to Node actually. I have the same problem if I try that whith a bash shell. I'm on windows 7.

Thank you for your help

fatal error when updating js/curl

step 3 of Running the demo, git submodule update produces the following result:
Cloning into js/curl...
remote: Counting objects: 2823, done.
remote: Compressing objects: 100% (1246/1246), done.
remote: Total 2823 (delta 1383), reused 2799 (delta 1359)
Receiving objects: 100% (2823/2823), 1.19 MiB | 729 KiB/s, done.
Resolving deltas: 100% (1383/1383), done.
fatal: reference is not a tree: 30587419d8b6a31ebe1b013597a47ff1e1d8b457
Unable to checkout '30587419d8b6a31ebe1b013597a47ff1e1d8b457' in submodule path 'js/curl'

Add `npm install` step to readme

Currently, the main project readme states that one should clone the repo, cd inside, and then run npm start. If fact, one must run npm install before npm start will work. The readme should be updated to reflect this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.