Пример #1
//Add() is the method that is called in response to an element being
//added to the collection (self.todos).  This is the "magic" turns an
//instance of todo into a view.
func (self *todoApp) Add(length int, newObj s5.Model) {
	model := newObj.(*todo)

	//note: There are two legal things that can be passed to any
	//note: of the tag creation methods.  Sadly, there is no way
	//note: to typecheck these until runtime (it is checked then).
	//note: The two legal things are some type of option, such as
	//note: Class() or ModelId() that affect the resulting tag.
	//note: One of the common types of "options" is something creates
	//note: a constraint between a dom "piece" of the tag being
	//note: being constructed and some value, usually in the model.
	//note: The other leagl item is another tag, that will be added
	//note: as a child of the one it is neted in.   This lack of type
	//note: safety has been chosen for convenience of notation.
	tree :=
			//LI: Pass in a model ID to generate unique id for this tag,
			//LI: and make easy to remove the whole subtree by id.
			//LI: constraint that toggles the completed property
			s5.CssExistence(completed, model.done),
			//LI: constraint that toggles the editing property
			s5.CssExistence(editing, model.editing),
				//DIV: just one CSS class to make it look nice
					//INPUT: has a CSS class "toggle" to make it look nice
					//INPUT: we force the "type" of this to be the constant "checkbox" (possibly overkill)
					s5.HtmlAttrEqual(s5.TYPE, s5.NewStringSimple("checkbox")),
					//INPUT: make the checked attr be equal to the model's done
					s5.PropEqual(s5.CHECKED, model.done),
					//INPUT: when clicked, it toggles the value on the model
					s5.Event(s5.CHANGE, func(e jquery.Event) {
					//LABEL: We use a constraint to bind the name attribute of the
					//LABEL: model to the label's displayed text.
					//LABEL: Double clicking on the label causes edit mode
					s5.Event(s5.DBLCLICK, func(jquery.Event) {
						//XXX UGH, don't have a handle to the input object
						in := s5.HtmlIdFromModel("INPUT", model).Dom()
					//BUTTON: destroy class makes it look nice
					//BUTTON: click function that calls remove on the list
					//BUTTON: element that was used to create this whole structure
					//JQUERY: Neither of the jquery params are used.
					s5.Event(s5.CLICK, func(jquery.Event) {
						//note: we are calling remove on the *collection* which
						//note: will end up calling the Remove() method of our
						//note: joiner.  If we don't tell the collection that the
						//note: model was removed, we could end up with a display
						//note: that doesn't correctly reflect the constraints
						//note: state (since these constraints would have dependencies
						//note: one items no longer visible).
				), //BUTTON
			), //DIV
				//INPUT: Use a model to make this input easy to find
				//INPUT: edit CSS class to make it look nice
				//INPUT: wire the placeholder to be name of the model... (overkill?)
				s5.HtmlAttrEqual(s5.PLACEHOLDER, model.name),
				//INPUT:the spec calls for escape to cancel editing with no change
				//INPUT:and for return to commit the changes EXCEPT if the
				//INPUT:user edited out all the text, then we should delete the
				//INPUT:whole thing
				//JQUERY: This uses the jquery selector to get the value of the input.
				//JQUERY: This uses the event object to get the keyboard code.
				s5.Event(s5.KEYDOWN, func(e jquery.Event) {
					//note: This type of "event handler" is the glue that
					//note: connects a user action to something that manipulates
					//note: the model.  Most event handlers do not need to
					//note: manipulate the view as well because they have constraints
					//note: that connect the model to the view.
					switch e.Which {
					case 13:
						//This conversion is a no-op other than the go language type.
						//The e.Target value is ALREADY a jquery object, but it's not
						//typed correctly for this function to use conveniently.
						j := s5.WrapJQuery(jquery.NewJQuery(e.Target))
						v := strings.Trim(j.Val(), CUTSET)
						//check for the special case of making a name==""
						if v == "" {
						} else {
							//just reset the model name and it propagates to display
					case 27:
			), //INPUT

Пример #2
//Start is called by concorde once the DOM is fully loaded.  Most of the
//application intialization code should be put in here.
func (self *todoApp) Start() {

	//Setup an event handler for the primary input field. The called func
	//creates model instance and puts in the list of todos.
	// JQUERY: Any use of jquery is suspect as it allows many non type-safe operations.
	primaryInput.Dom().On(s5.CHANGE, func(event jquery.Event) {
		if !self.createTodo(primaryInput.Dom().Val()) {

	//We need to attach the self.numNotDone string to the proper place
	//in the DOM.  Note that we use the lower-level jquery selector
	//(Select().Children())) plus NewTextAttr() we needed the string
	//child of span#todo-count (so we can't use HtmlId) and because
	//that object is directly in the HTML file.
	// JQUERY: Any use of jquery is suspect as it allows many non type-safe operations.
	selector := jquery.NewJQuery(todoCount.TagName() + "#" + todoCount.Id())
	todoCountSelect := selector.Children("strong")
	s5.Equality(s5.NewTextAttr(s5.WrapJQuery(todoCountSelect)), self.numNotDone)

	//We need to attach the self.plural string to the proper place
	//in the dom.
	s5.Equality(pluralSpan.TextAttribute(), self.plural)

	//These two calls attach the inverse of the empty attribute derived
	//from the model collection the display property (turning them on
	//when the list is not empty).  Note that we can't use the simpler
	//"Equality()" because we want to invert the value. The BooleanInverter
	//is a built in constraint function.

	//This connects the display property of the clearCompleted to the boolean
	//that is true if some of the elements are done.  This effectively
	//turns on the button when there are some elements in the list and
	//some of those elements are done.
	s5.Equality(clearCompleted.DisplayAttribute(), self.someDone)

	//This connects the display in the button to the number of done
	//elements.  Note that this wont be visible if there are no
	//done elements.
	s5.Equality(numCompleted.TextAttribute(), self.numDone)

	//This is the event handler for click on the clearCompleted
	//dom element. We just walk the list of objects building a kill list,
	//then we destroy all the items in the kill list
	//JQUERY: Neither of the jquery params are used.
	clearCompleted.Dom().On(s5.CLICK, func(jquery.Event) {
		all := self.todos.All()
		if len(all) == 0 {
		dead := make([]s5.Model, len(all))
		ct := 0
		for _, model := range all {
			if model.(*todo).done.Value() {
				dead[ct] = model
		for _, d := range dead {

	//toggleAll's behavior is to toggle any items that are not already
	//marked done, unless they are all marked done in which they should all
	//be umarked
	//JQUERY: Neither of the jquery params are used.
	toggleAll.Dom().On(s5.CLICK, func(jquery.Event) {
		desired := true
		//Compare the output of the constraints to see if all are done
		if self.todos.LengthAttribute().Value() == self.numDone.Value() {
			desired = false
		for _, m := range self.todos.All() {

	//These are discussed below. These are constraints that depend
	//on *all* the values in the list.