*UPDATE* (see below)
I understand the basics of KnockoutJS. When creating a table viewmodel, one would use <tr data-bind="foreach: rows">
Now I'm trying to abstract the table viewmodel, so that I can create multiple tables with the same behaviour (sorting, editing, pagination, etc..). So what I'm aiming for is something like this:
HTML
<div class="table a" data-bind="myTableBinding: aTableViewModel"></div>
<div class="table b" data-bind="myTableBinding: anotherTableViewmodel"></div>
Main ViewModel
var MainViewModel = function () {
this.aTableViewModel = new AbstractTableViewModel({
columns: [...]
initialSort: [...]
});
this.anotherTableViewModel = new AbstractTableViewModel({
columns: [...]
initialSort: [...]
});
};
My first try was mimicking the example [simpleGrid] plugin (# http://knockoutjs.com/examples/resources/knockout.simpleGrid.1.3.js) that the KnockoutJS docs use for the [Paged grid] example.
I'm not really sure, but I think that the basic concept of abstraction isn't represented well in this plugin. When I tried to include css class into the <th> elements like so: <th class="col col-id">, <th class="col col-name">, etc, I found out this wasn't easy (impossible?) using data-bind attributes.
The data-bind attributes probably shouldn't be used for this stuff through, because these classes won't change -- they are part of a higher abstraction level: we should actually insert these classes with jQuery.tmpl or Underscore's templating system. But then I got an error saying that [This template system doesn't support use of the foreach binding] (or something like that).
My second try therefore was to implement the abstraction as it should be implemented: with the table properties (columns, etc) at another "abstraction level" than the table data:
Create the basic <tr data-bind="foreach: rows"> html at instantiation of a new specific table view model, using an "abstract" template -- this I simply did with Underscore's _.template.
Let this specific viewmodel use the above html as usual.
In CoffeeScript:
do ->
ko.dataTable =
ViewModel: (config) ->
#id = config.id
#columns = config.columns
#pageSize = config.pageSize ? 9999
#sortColumn = ko.observable (config.sortColumn ? #columns[0].col)
#sortOrder = ko.observable (config.sortOrder ? "asc")
#data = ko.observableArray (config.data ? [])
null
ko.bindingHandlers.dataTable =
init: (el, acc) ->
viewModel = acc()
$(el).find("div:first").html dataTableTemplateMaker viewModel
# ??? [A] ko.applyBindings viewModel, $(el).find("table")[0]
# ??? [B] controlsDescendantBindings: yes
null
update: (el, acc) ->
viewModel = acc()
# ??? [C]
null
And then:
<div data-bind="dataTable: groupTable">
and:
class ViewModel
constructor: ->
#groupTable = new ko.dataTable.ViewModel
id: "grouptable"
columns: [
{ col: "num", title: "Groep", editable: yes }
{ col: "subject", title: "Vak" }
{ col: "year", title: "Jaar" }
{ col: "level", title: "Niveau" }
{ col: "day", title: "Dag" }
{ col: "hour", title: "Uur" }
{ col: "place", title: "Lokaal", editable: yes }
]
pageSize: 10
sortColumn: "num"
sortOrder: "asc"
data: [] # [D]
... in which ??? marks the spot(s) where my confusion lies.
Say I do not insert lines [A] and [B]. Then of course KnockoutJS tells me that the bindings are all messed up within the html for my specific viewmodel (which is inserted into the <div>. If I do insert lines [A] and [B], then it does work for initial data (at [D]), but after that does not respond.
Alltogether: I'm quite confused about something simple as abstracting a viewmodel. Isn't there a standard solution to this in KnockoutJS? (I've googled but couldn't quite find anything...) Or am I just messing it up myself (quite possible)? ;)
*UPDATE*
I solved the problem (but maybe it's not the best / well at all -- what is your opinion?), for completeness' sake: (a condensed version -- of course you'd probably also want to observe the rows individually etc..)
HTML (yes, that is purposefully a string passed to the binding handler)
<div data-bind="myTableBinding: 'viewModelPropertyHoldingTableViewModel'"></div>
CoffeeScript
class MainViewModel
constructor: ->
#viewModelPropertyHoldingTableViewModel = new TableViewModel <options>
null
class TableViewModel
constructor: (options) ->
#columns = options.columns
#rows = ko.observableArray (options.rows ? [])
[...]
null
tableTemplateMaker = _.template '
<table>
<thead>
<tr>
[% _.map(tableViewModel.columns, function (column) { %]
<th>[%= column.title %]</th>
[% } %]
</tr>
</thead>
<tbody data-bind="foreach: rows">
<tr>
[% _.map(tableViewModel.columns, function (column) { %]
<td data-bind="text: [%= column.id %]"></td>
[% } %]
</tr>
</tbody>
</table>
'
ko.bindingHandlers.myTableBinding =
init: (element, viewModelPropertyNameAccessor, _, mainViewModel) ->
tableViewModelProperty = viewModelPropertyNameAccessor()
tableViewModel = mainViewModel[tableViewModelProperty]
$(element).html tableTemplateMaker
tableViewModelProperty: tableViewModelProperty
tableViewModel: tableViewModel
null
m = new MainViewModel
ko.applyBindings m
m.viewModelPropertyHoldingTableViewModel.data.push [...]
Why reinvent the wheel? :P
https://github.com/CogShift/Knockout.Extensions
Related
I am new to meteor and have two Mongo collections, user and patient. I have concatenated them, in console this is displaying data but not in the html template. I am able to show data in objects in the console but not on my html template as seen in the image below:
What I have tried so far:
Template.showpatient.helpers({
'patientName': function () {
var tmpCollection = Patients.find() // `enter code here`
tmpCollection.forEach(function (myDoc) {
var a = Patients.find({"UserID": myDoc.UserID}).fetch().concat(user.find({"UserID": myDoc.UserID}).fetch())
return a;
console.log(a);
});
}
})
and at template side I am showing the data like this
{{#each patientName}}
<tr>
<td>
{{FirstName}}
</td>
<td class="f20">
{{LastName}}
</td>
<td>{{Gender}}</td>
<td>{{age}}</td>
<td>{{Email}}</td>
<td>{{HomePhone}}</td>
<td>{{CellPhone}}</td>
<td>{{Fax}}</td>
<td>{{AddressLine1}}</td>
<td>{{AddressLine2}}</td>
<td>{{City}}</td>
<td>{{State}}</td>
<td>{{Zip}}</td>
<td>{{IPAddressCreatedFrom}}</td>
<td>{{MaritalStatus}}</td>
Use the collection's map() method to iterate over the cursor, access the patient documents and use the underscore method _.extend() to generate a merged list of the patients and users joined on the UserID field. The following demonstrates this:
Template.showpatient.helpers({
'patientName': function () {
var merged = Patients.find().map(function(patient){
var user = User.findOne({"UserID": patient.UserID}) // get the
return _.extend(patient, user);
});
console.log(merged);
return merged;
}
})
Question is - how should I take data from this kind of inputs and pass it properly to DB (by SORM framework)?
I try to pass data to two tables in my DB, the result of my work is that while the values are properly inserted to the first table (Author), second one (Book) remains untouched.
Probably my problem is that I am not propely naming nested, repeated inputs on my page, but i cant find proper way to do this (I'm fairly new to Scala, so I probably lack of experience on this).
Ok(Json.toJson(author))
in addData in Application.scala shows me
{"name":"what","books":[]}
so I think, that the problem is in binding data from request.
I tried to follow examples here: How to model an entity with many children in Sorm? and here: https://www.playframework.com/documentation/2.1.1/ScalaForms, by operating on the template "play-scala" from Play framework, so i've got code like this:
Models are:
case class Author(name: String, books: Seq[Book]) {
}
object Author {
implicit val authorFormat = Json.format[Author]
}
case class Book(title: String ) {
}
object Book {
implicit val bookFormat = Json.format[Book]
}
case class AuthorBook(name: Author, title: Book) {
}
The scala.index.html
<table>
<tr>
<td>
On the left Author, on the right Book
<td>
</tr>
<tr>
<td>
<ul id="authors"></ul>
</td>
<td>
<ul id="books"></ul>
</td>
</tr>
</table>
<table>
<form action="#routes.Application.addData()" method ="post">
<tr>
<td>Author: <input name="author" type="text">
</td>
<td>Books: <input name="books.title[0]" type="text"><br><input name="books.title[1]" type="text">
</td>
</tr>
<tr>
<td>
<button>Submit</button>
</td>
</tr>
</form>
</table>
And Application.scala
class Application extends Controller {
def index = Action {
Ok(views.html.index("E-Library"))
}
val authorForm: Form[Author] = Form {
mapping(
"author" -> text,
"books" -> seq(
mapping(
"title" -> text)(Book.apply)(Book.unapply)
)
)(Author.apply)(Author.unapply)
}
def error = Action {
Ok("error")
}
def addData = Action { implicit request =>
authorForm.bindFromRequest.fold(
formWithErrors => {
BadRequest("Bad request!")
},
authorF => {
val author = DB.save(authorF)
Ok(Json.toJson(author))
//Redirect(routes.Application.index())
}
)
}
def getAuthor = Action {
val dataAuthor = DB.query[Author].fetch
Ok(Json.toJson(dataAuthor))
}
def getBook = Action {
val dataBook = DB.query[Book].fetch
Ok(Json.toJson(dataBook))
}
def getData = Action {
Redirect(routes.Application.index())
}
}
Found it!
As mentioned here: Nested form in Play! Scala 2.2
I needed to rename input names in my form from
book.title[0]
to
book[0].title.
Markdown has pipe table syntax but it's not enough for some cases.
| table | syntax | without multiline cell content |
So, we can use HTML table tags.
<table>
<tr>
<td>
```csharp
const int x = 3;
const string y = "foo";
readonly Object obj = getObject();
```
</td>
<td>
```nemerle
def x : int = 3;
def y : string = "foo";
def obj : Object = getObject();
```
</td>
<td>
Variables defined with <code>def</code> cannot be changed once defined. This is similar to <code>readonly</code> or <code>const</code> in C# or <code>final</code> in Java. Most variables in Nemerle aren't explicitly typed like this.
</td>
</tr>
But some time ago syntax highlighting was broken and this wiki page looks ugly now. Any ideas on how to fix this?
You can use <pre> in tables, as teh_senaus said. But if you do that, syntax highlighting won't work... or will it?
Through random experimentation I found that GitHub allows specifying it with <pre lang="csharp">. This has the same effect that ```csharp does of setting the syntax highlighting to C#.
This isn't really documented anywhere in GitHub's help center, nor in linguist's documentation. But it works, even inside of tables.
So for your example table, the new code would be as follows:
<table>
<tr>
<td>
<pre lang="csharp">
const int x = 3;
const string y = "foo";
readonly Object obj = getObject();
</pre>
</td>
<td>
<pre lang="nemerle">
def x : int = 3;
def y : string = "foo";
def obj : Object = getObject();
</pre>
</td>
<td>
Variables defined with <code>def</code> cannot be changed once defined. This is similar to <code>readonly</code> or <code>const</code> in C# or <code>final</code> in Java. Most variables in Nemerle aren't explicitly typed like this.
</td>
</tr>
Add a blank line between the <td> and the code block.
Here's the fixed markdown:
<table>
<tr>
<td>
```csharp
const int x = 3;
const string y = "foo";
readonly Object obj = getObject();
```
</td>
<td>
```nemerle
def x : int = 3;
def y : string = "foo";
def obj : Object = getObject();
```
</td>
<td>
Variables defined with <code>def</code> cannot be changed once defined. This is similar to <code>readonly</code> or <code>const</code> in C# or <code>final</code> in Java. Most variables in Nemerle aren't explicitly typed like this.
</td>
</tr>
</table>
and the result:
You can use <pre>. Syntax highlighting won't work, but at least it will be formatted properly.
<td><pre>
const int x = 3;
const string y = "foo";
readonly Object obj = getObject();
</pre></td>
Another way is using multiple ` and <br>, but Syntax highlighting won't work .
|1|2|3
-|-|-
`const int x = 3;`<br>` const string y = "foo";`<br>`readonly Object obj =getObject();`|`def x : int = 3;`<br>`def y : string = "foo";`<br>`def obj : Object = getObject(); `|Variables defined with `def` cannot be changed once defined. This is similar to `readonly` or `const` in C# or `final` in Java. Most variables in Nemerle aren't explicitly typed like this.explicitly typed like this.
The trick is to use the backticks around your code, while wrapping it all with a <pre> tag, like so:
<pre lang=html>`<input readonly>`</pre>
Here's a screenshot of how it renders, from my own use case:
You can also :
A | B | C
-- | -- | --
x | y | Some code : <pre lang=php>function sayHello($someArg)</pre>
1 | 2 | 3
I have a question for which I couldn't find an answer anywhere in the web. I have a lift-based web application written on Scala. There is a form in my application which is driven by a snippet. I use Mapper for my models, and I use override def validations ... to define model's validation parameters. The template code looks like this:
<form class="lift:GroupManager.addGroup?form=POST&action=add">
<div class="row collapse" style="width: 30em">
<div class="small-8 columns">
<span class="group-name prefix radius"></span>
</div>
<div class="small-4 columns">
<span class="submit button small postfix radius">Add New Group</span>
</div>
</div>
</span>
</form>
The snippet code:
def addGroup = {
object groupName extends RequestVar[String]("")
def doAddGroup() = {
val group = Group.create.createdAt(new java.util.Date).groupName(groupName.is)
group.validate match {
case Nil => {
group.save
S.notice("Group '" + group.groupName.is + "' has been added successfully")
}
case errors:List[FieldError] => {
S.error(errors)
}
}
}
".group-name" #> SHtml.text(groupName.is, groupName(_), "id" -> "group-name") &
".submit" #> SHtml.submit("Add group", doAddGroup)
}
The model's validations override:
object groupName extends MappedString(this, 700) {
override def dbColumnName = "group_name"
override def dbNotNull_? = true
override def validations = valMinLen(1, S.?("Group name should not be empty!")) _ ::
valMaxLen(700, S.?("Group name should not be more than 700 characters long!")) _ ::
valUnique(S.?("Group name should be unique!")) _ ::
super.validations
}
Now everything is perfect except the fact that when invalid data is provided, S.error is used to inform user about problems. What I want to do is to highlight the form element (by applying class="error") in which the data is invalid and add a <span> element with the error message in front of the form element. Is there a simple way to do that with Lift framework? Or I should just traverse through errors:List[FieldError] and fetch field information from that array, which does not seem too elegant to me?
I have defined this function to build a list os students, each one with his name, surname and an image, that depending an attribute can be a tick or a cross. I want to assign to the tag academic(my image) the onClick event to call a function in my snippet called academic(Student s) which receives a student,and then redirect to another page. Can anyone help me??
def students(xhtml: NodeSeq) = {
def bindStudents(template: NodeSeq): NodeSeq = {
studentsList.flatMap {
s => bind("studentTag", template, "name" -> s.Name, "surname" -> s.Surname,
AttrBindParam("academic", if (s.HasIssue("Academic")) tickUrl else crossUrl, "src"),
)}
}
bind("list", xhtml, "studentsList" -> bindStudents _)
}
def academic(s:Student)={
//do some stuff with s
redirectTo("/OtherPage.html")
}
HTML CODE
<list:studentsList>
<tr>
<td> <studentTag:name /> </td>
<td> <studentTag:surname /> </td>
<td>
<img studentTag:academic=""/>
</td>
</list:studentsList>
There is a method in SHtml called 'onEvent'.
I'm not very sure how to do this in the old way, but if you are using new designer-friendly CSS binding helpers, it will be very simple:
HTML template:
<div data-lift="MySnippet">
Name:<span id="name">Student Name</span>
SurName:<div class="surname">Surname</span>
<img src="imageURL" class="clickable"/>
</div>
Here is the snippet:
class MySnippet
{
val studentName = "whatever"
val surName = "..."
def onClickCallback(s: String): JsCmd = {
// Do what ever you want, BTW, I never have idea
// what the ``s`` the lift will passed in.
Alert('Hey, you clicked me.') // This is the javascript that browser will execute when user click on the element.
}
// Note there is no argument list
def render = {
"#name" #> studentName & // Replace the HTML tag with id="name" to studentName
".surname" #> surName & // Replace the HTML tag with class=surname to surName
".clickable [onClick]" #> SHtml.onEvent(onClickCallback) // Append onClick attribute to HTML tag that has "clickable" class, and it will calle onClickCallable in your snippet.
}
}
Update
Sorry, I didn't notice that you are binding to a list. But since there is curried function, it would not be too difficult either.
HTML code:
<table data-lift="MySnippet">
<tr>
<td><span class="name">Name</span></td>
<td><span class="surname">SurName</span></td>
<td><img src="test.jpg"/> </td>
</tr>
</talbe>
case class Student(name: String, surname: String)
class MySnippet
{
def onClickCallback(student: Student)(s: String): JsCmd = {
Alert("You Click:" + student)
}
val xs = Student("Brian", "Hsu") :: Student("David", "Walter") :: Nil
def render = {
"tr" #> xs.map { student =>
".name" #> student.name &
".surname" #> student.surname &
".clickable [onClick]" #> SHtml.onEvent(onClickCallback(student)_)
}
}
}