Exemplo n.º 1
0
//newTodo creates a new todo list item for a given string.  Note that the
//three primary elements are Attributes so the constraint system can operate
//on them.  The value provided is used to initialize the displayed text.
func newTodo(raw string) *todo {
	result := &todo{
		name:    s5.NewStringSimple(raw),
		done:    s5.NewBooleanSimple(false),
		editing: s5.NewBooleanSimple(false),
	}
	result.modName = s5.NewModelName(result)
	return result
}
Exemplo n.º 2
0
//newApp creates a new instance of the application object, properly
//initialized
func newApp() *todoApp {
	result := &todoApp{}
	//init the list, setting our own object as the joiner (we meet
	//the interface Joiner)
	result.todos = s5.NewList(result)

	//create initial values of attributes
	result.numNotDone = s5.NewIntegerSimple(0)
	result.plural = s5.NewStringSimple("")
	result.someDone = s5.NewBooleanSimple(false)
	result.numDone = s5.NewIntegerSimple(0)

	//done create app object
	return result
}
Exemplo n.º 3
0
//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:
	//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:
	//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 :=
		s5.LI(
			//LI: Pass in a model ID to generate unique id for this tag,
			//LI: and make easy to remove the whole subtree by id.
			s5.ModelId(model),
			//LI: constraint that toggles the completed property
			s5.CssExistence(completed, model.done),
			//LI: constraint that toggles the editing property
			s5.CssExistence(editing, model.editing),
			s5.DIV(
				//DIV: just one CSS class to make it look nice
				s5.Class(view),
				s5.INPUT(
					//INPUT: has a CSS class "toggle" to make it look nice
					s5.Class(toggle),
					//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) {
						model.done.Set(!model.done.Value())
					}),
				),
				s5.LABEL(
					//LABEL: We use a constraint to bind the name attribute of the
					//LABEL: model to the label's displayed text.
					s5.TextEqual(model.name),
					//LABEL: Double clicking on the label causes edit mode
					s5.Event(s5.DBLCLICK, func(jquery.Event) {
						model.editing.Set(true)
						//XXX UGH, don't have a handle to the input object
						in := s5.HtmlIdFromModel("INPUT", model).Dom()
						in.SetVal(model.name.Value())
						in.Focus()
						in.Select()
					}),
				),
				s5.BUTTON(
					//BUTTON: destroy class makes it look nice
					s5.Class(destroy),
					//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).
						self.todos.Remove(model)
					}),
				), //BUTTON
			), //DIV
			s5.INPUT(
				//INPUT: Use a model to make this input easy to find
				s5.ModelId(model),
				//INPUT: edit CSS class to make it look nice
				s5.Class(edit),
				//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 == "" {
							self.todos.Remove(model)
						} else {
							//just reset the model name and it propagates to display
							model.name.Set(v)
						}
						j.Blur()
						fallthrough
					case 27:
						model.editing.Set(false)
						primaryInput.Dom().Focus()
					}
				}),
			), //INPUT
		).Build()

	listContainer.Dom().Append(tree)
}