How to use knockout mapping? - mvvm

I have applied bindings to the following view model
var groupDeleteViewModel = {
group: { Id: ko.observable(), Name: ko.observable(), Members: ko.observableArray() },
load: function (item) {
debugger
},
remove: function (item) {
groupDeleteViewModel.group.Id(item.Id());
groupDeleteViewModel.group.Name(item.Name());
groupDeleteViewModel.group.Members(item.Members());
$("#groupDelete").show();
},
cancel: function () {
$("#groupDelete").hide();
}
}
the remove function loads the view with the data that has been passed in item to the remove function.
<div id="groupDelete" class="pop-window filter-view">
<h2>
Delete Group
</h2>
<table>
<tr>
<th>
Name
</th>
<td>
<span data-bind="text:group.Name" />
</td>
</tr>
<!--ko foreach: group.Members-->
<tr>
<th>
</th>
<td>
<div data-bind="text:Name" class="grey-border">
</div>
</td>
<td>
</td>
</tr>
<!--/ko-->
</table>
<span class="centeralign">
<input type="button" value="Delete" data-bind="click: remove" class="delete" />
<input type="button" value="Cancel" data-bind="click: cancel" />
</span>
</div>
I don't want to keep mapping each element of item to each element of groupDeleteViewmodel.group. I have done it at a lot of other places also in the code which has made the code really messy. I want to use the ko.mapping plugin to do the same thing.
Till now what I have tried is -
remove:function (item){
var data = ko.mapping.toJS(item);
ko.mapping.fromJS(data, groupDeleteViewModel.group);
$("#groupDelete").show();
}
But this just does not work. i really don't know why. It should have worked ideally.
Can someone tell what is the right way of using ko.mapping in such a case?
Thanks.

The mapping plugin is looking for mapping data already present in groupDeleteViewModel.group, but it doesn't find it, because you didn't initialize groupDeleteViewModel.group using the mapping plugin.
So instead of initializing group like you did:
group: { Id: ko.observable(), Name: ko.observable(), Members: ko.observableArray() }
initialize it using the mapping plugin:
group: ko.mapping.fromJS({ Id: undefined, Name: undefined, Members: [] })
And this is me fiddling around with your code: fiddle

Related

Can not edit the form from sails.js

Hey guys I'm really new at sails.js and I'm using version 1.0 of it. I just tried to create a CRUD application on adding article to my list and showing them. All goes right but when I want to delete or edit it says 404 error. I am going through this tutorial series and as I know this series is using sails js version 0.12.
I am using mongodb as my database as a default. Data insertion is going well but problem is when I wanted to edit or delete it. Here I think the problem is getting the id value from the url. I skipped the edit portion here and posted the delete portion here.
api/controllers/ ArticleController.js
module.exports = {
delete: function(req, res){
Articles.destroy({id:req.params.id}).exec(function(err){
if(err){
res.send(500,'Database Error');
}
res.redirect('/articles/list');
});
return false;
}
};
api/models/ Articles.js
module.exports = {
attributes: {
title:{
type:'string'
},
body:{
type:'string'
}
},
};
views/pages list.ejs
<h1 class="display-4">Articles</h1>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody>
<% articles.forEach(function(article){ %>
<tr>
<td><%= article.id %></td>
<td><%= article.title %></td>
<td>
<form class="d-inline" action="/articles/delete/<%= article.id %>" method="POST">
<input type="submit" class="btn btn-danger" value="Delete">
</form>
</td>
</tr>
<% }) %>
</tbody>
</table>
config/route.js
module.exports.routes = {
'/': {
view: 'pages/homepage'
},
};

Unable to show new properties in User schema

I'm working within a MEAN app, and I'm modifying the User module. I modified the User Schema to contain 2 new properties: casetype and Steps. casetype is just a string, and Steps is an array. I created a view to show these along with a controller. Neither are shown on the page when I run the server. If I try to show any of the preexisting properties of the logged in user, they work fine, it just seems to be the new stuff I added that doesn't work. What's more puzzling is that they save just fine to the database, and when using Mongo shell I can find the user by querying based on casetype.
view:
<div class="row">
<h1 ng-bind="vm.user.casetype"></h1>
</div>
<div class="row">
<div class="col-md-6">
<div class="tableWrapper">
<table id = "table_id" class="table table-hover">
<thead>
<tr>
<th>Step</th>
<th>Todo</th>
<th>Completed?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="step in vm.user.steps.type | filter: query" >
<td>{{step.num}}</td>
<td>{{step.text}}</td>
<td class = "y_n">{{step.completed}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
UserSchema additions:
//case type
casetype: {
type: String,
default: 'Default Case Type'
},
//steps for case tracking
steps: {
type: [{
num: {
type: Number,
//default: 0
},
text: {
type: String,
trim: true,
//default: 'Todo for step'
},
completed: {
type: Boolean,
//default: false
}
}],
default: [{
num: 0,
text: 'todo for step',
completed: false
}]
},
Steps is an object. you need to render steps.type which is an array:
<tbody>
<tr ng-repeat="t in vm.user.steps.type | filter: query" >
<td>{{t.num}}</td>
<td>{{t.text}}</td>
<td class = "y_n">{{t.completed}}</td>
</tr>
</tbody>

Adding field to object array in AngularJS

I have a form containing an ng-repeat, with some input hidden field that i need to pass to the server.
This is the code of the page:
<form ng-controller="FrmController" method="post">
<div class="orderlist">
<table class="table gas">
<thead>
<tr>
<th scope="col" ng-click="orderByField='image'; reverseSort = !reverseSort">Immagine
<span ng-show="orderByField == 'image'" class="listorder">
<span ng-show="!reverseSort"><i class="fa fa-sort-desc"></i> </span>
<span ng-show="reverseSort"><i class="fa fa-sort-asc"></i></span>
</span>
</th>
[...]
</thead>
<tbody id="gasbody" ng-repeat="product in products | filter: searchQuery | orderBy:orderByField:reverseSort" class="repeated-item">
<tr>
<input type="hidden" name="form-x-id" ng-model="form.form-x-id">
(x represent the index of the ng-repeat)
[...]
<button ng-click="addtoCart();" class="def-btn" type="submit"><span class="glyphicon glyphicon-shopping-cart"></span> Add to cart</button>
</form>
This is the code of the controller:
function FrmController($scope,$http,transformRequestAsFormPost){
$scope.form = {};
$scope.addtoCart = function() {
$http({
method: 'POST',
url: '',
data: $.param($scope.form),
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
});
}
}
I need to send data in this format:
form-1-id: value
form-2-id: value2
form-3-id: value3
$scope.form = {form-1-id: value,
form-2-id: value2,
form-3-id: value3, ...
... form-n-id: valuen}
where n represent the lenght of ng-repeat.
ecc....
How can i dynamically add field to a $scope binded to a ng-model?
Is it possible?
Thanks
I dont underestant exactly , what do you want ?
Do you want index of ng-repeat?
You can just use $index like this
<input name="form-{{$index}}-id" type="Hidden" ng-model="form[$index]">
Or do you want to push something to your form dynamically? You can use:
$scope.addToForm=function(newItem){
$scope.form.push(newItem)
}
Of course you have to change your form={} , to form=[]

How to handle one to many relation ship Knockout-Entity Framework

So I have I have a simple structure where one purchase have a collection of expenses, and each expense have an account(plastic, cash, plastic#2...).
So the json my api gets is similar to this:
[
{"$id":"1","Id":1,"Name":"apple","Value":100.0,"AccountId":1,"Account":
{"$id":"2","Id":1,"Name":"Cash"}},
{"$id":"3","Id":2,"Name":"pear","Value":50.0,"AccountId":1,"Account":
{"$ref":"2"}},
{"$id":"4","Id":3,"Name":"raspberry","Value":10.0,"AccountId":1,"Account":
{"$ref":"2"}}
]
I see my json is not writing my cash account each time it needs it, it is refering it with
{"$ref":"2"}
where
{"$id":"2","Id":1,"Name":"Cash"}
so when I render my table with this html:
<table>
<tbody data-bind="foreach: gastos">
<tr>
<td data-bind="text: $data.id"></td>
<td data-bind="text: $data.name"></td>
<td data-bind="text: $data.value"></td>
<td data-bind="text: $data.account.Name"></td>
<td>
<button type="button" class="btn btn-xs">
<i class="glyphicon glyphicon-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
I get this, because the account for pear, and raspberry are nulls:
So how do you handle $ref in knockout?
I am mapping to 'gastos' this way:
$.getJSON('#ViewBag.GastosUrl', function (data) {
data.forEach(function(o) {
gastos.push(new gastoVM(o.Id, o.Name, o.Value, o.Account));
});
});
var gastoVM = function(Id, Name, Value, Account) {
var self = this;
self.id = Id;
self.name = Name;
self.value = Value;
self.account = Account;
};
thanks.
I'm not familiar with entity-framework but with the data as provided, a couple options available (JSFiddle):
Build up the account information alongside the gastos. And only provide the $id or $ref for later referencing.
var vm = {
gastos: [],
accounts: {},
accountLookup: function(accountId){
return this.accounts[accountId];
}
}
//... inside AJAX call
var accountId = o.Account["$id"]
if(accountId)
{
vm.accounts[accountId] = o.Account;
}
Use a ko utility method to lookup the account from within your array.
accountLookupByUtil: function(accountId) {
var gasto = ko.utils.arrayFirst(this.gastos, function(item) {
if(item.account['$id'] == accountId)
{
return item
}
});
return gasto.account;
}
From the html:
<td data-bind="text: $root.accountLookup($data.accountId).Name"></td>
<td data-bind="text: $root.accountLookupByUtil($data.accountId).Name"></td>
Note: Both methods are available in the fiddle, thus some properties are provided that would not be necessary depending upon the method used.

Prototype focusFirstElement() causes form to 'disappear' in IE

Strange bug I'm getting. I have a login form that is hidden by default, once a user clicks on an observed element, the element containing the form slides down (using Effect.BlidnDown in Scriptaculous)
Here is the function, which is part of the 'Interface' object:
revealLoginForm: function() {
$('a_login').blur();
if (!$('hform').visible()) {
Effect.BlindDown('hform', {
duration: 0.4,
afterFinish: function() {
$('f_login').focusFirstElement();
}
});
} else {
$('f_login').focusFirstElement();
}
},
The event observation:
Event.observe('a_login', 'click', Interface.revealLoginForm);
And the HTML:
<a id='a_login' href='javascript:void(0);'>Login to My Account</a>
....
<div id='hform' style='display:none;'>
<form id='f_login' action='...' method='post' name='sidelogin'>
<table cellspacing='0' class='login'>
<tr>
<th>Login ID:</th><td><input style='padding:2px;' type='text' size='18' name='enc_loginname' /></td>
</tr>
<tr>
<th>Password:</th><td><input style='padding:2px;' type='password' size='18' name='enc_password' /></td>
</tr>
<tr>
<th></th><td><input type='submit' class='submit' value='Login ยป' /></td>
</tr>
</table>
</form>
</div>
If I disable the $('f_login').focusFirstElement(); command in revealLoginForm(), the form does not disappear. What's strange is, the other call to this function does not adversely affect the element, it only seems to happen when called within the afterFinish callback to the Effect.BlindDown function.
Any ideas? It would be nice to have the first element of this form selected once the form has appeared. This works fine in Firefox.. it's just IE (Im using IE7) that's causing problems (go figure...)