Adding Operations to Knockout Mapping Object - javascript

I am struggling with adding operations to my Knockout Mapping object.
Here is where I am at right now - and I am attempting to follow the Knockout: Mapping API information.
This is the HTML first:
+ Add Contact
<div id="ContactList">
<table>
<thead>
<tr>
<th>ID</th><th>Name</th><th></th>
</tr>
</thead>
<tbody data-bind="foreach: $root">
<tr>
<td data-bind="text: ContactID"></td>
<td data-bind="text: DisplayName"></td>
<td></td>
</tr>
</tbody>
</table>
</div>
And this is my first attempt at wiring up Knockout:
var baseModel = {
addContact: function() {
alert('Woo!');
}
};
contacts = ko.mapping.fromJS('[{"ContactID":6,"DisplayName":"Doe, John"},{"ContactID":7,"DisplayName":"Rogers, Mister"}]', { }, baseModel);
ko.applyBindings(contacts);
Now, if I remove the additional arguments ({} and baseModel) from the ko.mapping.fromJS, all my data is displayed in the table correctly. However (and as to be expected), I get a binding error with the "Add Contact" link. If I add baseModel back in, clicking the "Add Contact" link works, but no data displays. I get no errors one way or another, so I'm a bit stumped on what's going on there.
I'm new to KO, so I feel like I'm missing something pretty simple. I've searched across StackOverflow, but, at this point, I'm just plugging code in to see what works and not actually understanding what is happening here. Can someone provide some additional explanation and point me in the right direction? Thanks.

You're using ko.mapping.fromJS but you're passing a string, not an object/array. Either use fromJSON or pass in a real array.
Also, you should applyBindings on baseModel, and contacts should be a field of the VM. Then you can use foreach: contacts instead of your weird foreach: $root.
See fiddle with those changes: http://jsfiddle.net/antishok/kR4jc/3/
EDIT - Updating your answer with your code so the question can stand on its own.
JavaScript
var baseModel = {
addContact: function() {
alert('Woo!');
}
};
baseModel.contacts = ko.mapping.fromJSON('[{"ContactID":6,"DisplayName":"Doe, John"},{"ContactID":7,"DisplayName":"Rogers, Mister"}]');
ko.applyBindings(baseModel);​
HTML
+ Add Contact
<div id="ContactList">
<table>
<thead>
<tr>
<th>ID</th><th>Name</th><th></th>
</tr>
</thead>
<tbody data-bind="foreach: contacts">
<tr>
<td data-bind="text: ContactID"></td>
<td data-bind="text: DisplayName"></td>
<td></td>
</tr>
</tbody>
</table>
</div>​

Related

Cypress iterate table rows get specific element in cells

I'm trying to iterate through table rows and get each row which includes a specific value,
but it doesn't work for me.
I'm using .each() to iterate the rows and .within() on each $el,
inside that, I use cy.get('td').eq(1).contains('hello') but I the get assertion error:
Timed out retrying: Expected to find content: 'hello' within the element: <td> but never did.
when I console.log cy.get('td').eq(1) it yields the desired cell in each row and the test passes, so I don't understand why chaining .contains() doesn't work...
it('get element in table', () => {
cy.visit('http://localhost:3000/');
cy.get('tbody tr').each(($el) => {
cy.wrap($el).within(() => {
cy.get('td').eq(1).contains('hello') // contains() doesn't work
})
})
});
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>hello</td>
<td>$80</td>
</tr>
<tr>
<td>$10</td>
<td>hello</td>
</tr>
</tbody>
</table>
should('have.text', text) should work
cy.get('td').eq(1).should('have.text', 'hello')
If there's whitespace around text, use contain.text
cy.get('td').eq(1).should('contain.text', 'hello')
The simple answer is: don't :)
To be more specific use html attribute selection instead. The convention is to have an attribute named data-cy. Furthermore, I discovered it convenient to have a data-cy-identifier for when selecting specific rows. Since I'm not sure what you're trying with your code, I'll use a similar example that can hopefully get you going:
<table data-cy="expences">
<tr>
<td data-cy="month">January</td>
<td data-cy="price">$100</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">Feburary</td>
<td data-cy="price">$80</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">March</td>
<td data-cy="price">$10</td>
</tr>
</table>
You can of course do all sorts of combinations of this, but now you can do more specific and useful selections, such as:
cy.get('[data-cy="expenses"]').find('[data-cy="discounted"]').find('[data-cy="price"]').should(...)
And similar. This is flexible, because it reflects the structure of your data, and not the presentation, so you can change this to a list or whatever later. It avoids selecting of volatile things, so it's also more robust. It also uses a hierarchy rather than overly specific selectors.
The idea of adding things like data-cy-identifier allows you to do selections by ID (you can propagate it using javascript, angular, vue or whatever you use) and then checking things like the contents of a row with logically related items.
Hope it can get you going. Also I can recommend reading: https://docs.cypress.io/guides/references/best-practices.html

Creating table with unknown headers with angular.js

I have a .json with unknown attributes and want to graph them. In my controller I have already extracted the headers and saved them as table_headers, from the doubleObjects Object. The data looks like this:
$scope.items = {[
...,
{doubleObjects: [{...}, {...}, ...]},
...,
]};
This is what I'm trying to output:
<table style="width:100%" border="1px solid black">
<tr>
<th ng-repeat="y in table_headers">{{y}}</th>
</tr>
<tr ng-repeat="x in items.doubleObjects">
<td ng-repeat="y in table_headers">{{x.y}}</td>
</tr>
</table>
So I would like the entire doubleObjects object to be tabulated. It wont let me do:
{{x.y}}
If I do:
{{x.measurement}}
or another attribute that is in doubleObjects then its all fine. But I cant ensure that the "measurement" attribute will be there. (For my test data it is.)
Thoughts and suggestions are most welcome.
You should not use x and y that way {{x.y}} use it this way {{x[y]}} or like the other way shown in this jsfiddle which shows both possibilities. I stated both possibilities, as I didn't really understood what you are looking for and I'm not allowed to comment your post.
<tr ng-repeat="x in items.doubleObjects">
<td ng-repeat="y in table_headers">{{x.measurement}}.{{y}}</td>
<td ng-repeat="y in table_headers">{{x[y]}}</td>
</tr>

split a string (ng-model) created inside ng-repeat directive

I start to play with angularjs, and having this data model :
friends = [
{name:'John**amine**allo'},
{name:'Mary**aaa**mmm'},
{name:'Mike**bb**kkk'}
]
I would like to split each name by '**' to create a table of names.
I don't know if there is more straightforward way to do this ( maybe using some filter in angular-filter module) but here what I have so far tried:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.split('**')">
{{y}}
</td>
</tr>
thanks in advance for any help.
You need to split name property if the x elements:
<td ng-repeat="y in x.name.split('**')">{{y}}</td>
However this is probably not very good idea what you are trying to do. First of all, you need to take care of proper table structure (correct header colspan values). Secondary, it's better to render already processed data structure, rather then converting values inside templates. It just makes it overcomplicated, not to mention makes watch expression slower.
Demo: http://plnkr.co/edit/5PXTW1gjpKwg7e3S6hRM?p=preview
You can use a custom filter to specify the separator:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.name | mysplit:'**'">
{{y}}
</td>
</tr>
</table>
JS:
app.filter('mysplit', function() {
return function(data, sep) {
return data.split(sep);
}
});
Plunker

AngularJS data binding within another data binding

I just stepped into the AngularJS world, and need a solution for the application I am working on.
So here is the definition of the module.
var samplesApp = angular.module('samples', []);
samplesApp.controller('samplesController', function ($scope) {
var jsonObj=
[
{"ACTION":"UPDATE","ID":"22","ROUTE":"0015"},
{"ACTION":"DELETE","ID":"20","ROUTE":"0015"},
{"ACTION":"UPDATE","ID":"19","ROUTE":"0015"}
]
$scope.records = jsonObj;
var columnNames = [];
for (var key in jsonObj[0]) {
columnNames.push(key);
}
$scope.columnNames = columnNames;
});
The $scope JSON object is a part of the output of the real data that comes from database. and I need to put these data into a table dynamically
The html is like
<table>
<thead>
<th data-ng-repeat="column in columnNames">{{column}}</th>
</thead>
<tr data-ng-repeat="record in records">
<td data-ng-repeat="column in columnNames">{{ record.{{ column }} }}</td>
</tr>
</table>
Because I have no idea what the column name is, so I made a process to get all the column names and push them into $scope.columnNames. and then bind it to the table header. There is no problem for this part. The issue is I don't know how to get the value coresponse to the specific column. I was trying to do it like this:
<td data-ng-repeat="column in columnNames">{{ record.{{ column }} }}</td>
But apparently it is not working.
Can someone give me some advice? really appreciate it.
Try using brackets, like this:
<tr data-ng-repeat="record in AllRecords">
<td data-ng-repeat="column in columnNames">{{ record[column] }}</td>
</tr>
The expressions inside {{ }} (moustaches?) are evaluated pretty narrowly to how regular javascript is evaluated.
Accessing with the record[column] operator is OK, but
you could try this other alternative. In this example, the semantics
of the code is a bit better, and you have less coupling (your rows just
depend on the data, not the variable used in the header):
<table>
<tr>
<th data-ng-repeat="column in columnNames">{{column}}</th>
</tr>
<tr data-ng-repeat="record in records">
<td data-ng-repeat="(key,value) in record">{{value}}</td>
</tr>
</table>
Here's the working example with your data: http://plnkr.co/edit/CskLQ2ZZlYIURdNPXiZt?p=preview
Here's the Angular docs for the ngRepeat directive (look for "key, value")

display nested arrays in knockout.js

I have a json object returned via jquery ajax from an asmx service. When I attempt to display it in a table, nothing displays. What might I be doing wrong?
Thanks in advance.
The returned json is ...
[{\"id\":1488,\"name\":\"Baby Books\",
\"categories\":
[{\"id\":152,\"name\":\"Activity Books\",\"value\":\"Ignore\"},
{\"id\":167,\"name\":\"Bedtime and Dreams\",\"value\":\"Ignore\"}]},
{\"id\":1489,\"name\":\"Story Books\",
\"categories\":
[{\"id\":1506,\"name\":\"Games - Floor Puzzles\",\"value\":\"Ignore\"}]}]
the js display code is (where gorupJson is returned from the call) ...
var viewModel = {
groups: ko.observableArray([]),
};
viewModel.groups(groupJson)
ko.applyBindings(viewModel);
the page content is ...
<table width="100%" >
<tbody data-bind="foreach: groups" style="width:100%">
<tr>
<td data-bind="text: name"></td>
<td>
<ul data-bind="foreach: categories">
<li>
<a><span data-bind="text: name"></span></a>
</li>
</ul>
</tbody>
</table>
Your json is not automatically converted into ko collection.
So you need to apply a mapping to this conversion, probably like this ko.mapping.fromJS(data). When you do it, your fields id, name, value will become in ko.observable
If you want to know more about mapping plugin in knockout, take a look at http://knockoutjs.com/documentation/plugins-mapping.html
First, your JSON is invalid.
Check out the fiddle: http://jsfiddle.net/YNdmh/
var groupJson = [{"id":1488,"name":"Baby Books","categories":
[{"id":152,"name":"Activity Books","value":"Ignore"},
{"id":167,"name":"Bedtime and Dreams","value":"Ignore"}]},
{"id":1489,"name":"Story Books","categories":
[{"id":1506,"name":"Games - Floor Puzzles","value":"Ignore"}]}]
You're missing closing tags for <td> and <tr>
The slashes in json have to go aswell.
http://jsfiddle.net/yhBvr/

Categories

Resources