This is my div.
<div id="storyBody" data-bind="foreach: CurrentStory.Paragraphs">
<p data-bind="text: $data">
</p>
</div>
Here is my model:
$(function ()
{
var currentUser = "";
nm.Story = function()
{
this.Paragraphs = ko.observableArray([]);
this.CurrentEditorId = ko.observable();
this.LastEditorId = ko.observable();
};
nm.StoryPreview = function()
{
this.Preview = "";
this.documentId = 0;
};
nm.vm = function ()
{
var UserStoryList = ko.observableArray([]),
CurrentStory = ko.observable(),
GetCurrentStory = function (data) {
nm.getStory(data.documentId, GetCurrentStoryCallback, GetCurrentStoryErrorCallback)
},
GetCurrentStoryCallback = function (data) {
var story = new nm.Story().CurrentEditorId(data.CurrentEditorId)
.LastEditorId(data.LastEditorId);
story.Paragraphs(data.Paragraphs);
CurrentStory(story);
},
GetCurrentStoryErrorCallback = function (data) {
},
LoadUserStoriesList = function() {
nm.getStories(LoadUserStoriesListCallback, LoadUserStoriesListErrorCallback);
},
LoadUserStoriesListCallback = function(data)
{
$.each(data, function (index, value) {
var storyPreview = new nm.StoryPreview();
storyPreview.Preview = value.Preview;
storyPreview.documentId = value.DocumentId;
UserStoryList.push(storyPreview);
});
},
LoadUserStoriesListErrorCallback = function (data)
{
};
return {
UserStoryList: UserStoryList,
CurrentStory: CurrentStory,
LoadUserStoriesList : LoadUserStoriesList,
LoadUserStoriesListCallback: LoadUserStoriesListCallback,
GetCurrentStory: GetCurrentStory
}
}();
nm.vm.LoadUserStoriesList();
ko.applyBindings(nm.vm);
});
Seems like this should work, but it doesn't. Any ideas?
You are missing some parenthesis.
Could you try with:
<div id="storyBody" data-bind="foreach: CurrentStory().Paragraphs">
<p data-bind="text: $data">
</p>
</div>
Related
I have a fairly simple task list using angularjs and I would like to save the time when each task was created and update that time if the task is edited.
I'm thinking I could use something like this to display the current time, but I'm not sure how to do it on save/edit.
HTML:
<div ng-controller="TodoListController">
<form ng-submit="addTodo()" name="form">
<input type="text" ng-model="todoText" size="30" placeholder="Add New Entry" required id="textField" ng-model="myVar">
<input class="btn-primary" type="submit" value="Save">
</form>
<ul class="unstyled">
<li ng-repeat="todo in todos | orderBy : $index:true">
<button type="button" class="close" aria-label="Close" ng-click="remove(todo)">
<span aria-hidden="true">×</span>
</button>
<span class="done-{{todo.done}}" ng-style="todo.customStyle" ng-hide="todo.editing" ng-click="updateVar($event)">{{todo.text}}</span>
<input type="text" ng-show="todo.editing" ng-model="todo.text">
<button type="submit" ng-hide="todo.editing" ng-click="change(todo); todo.editing === true">Edit</button>
<button type="submit" ng-show="todo.editing" ng-click="save($index); todo.editing === false">Save</button>
<button type="submit" ng-show="todo.editing" ng-click="cancel($index); todo.editing === false">Cancel</button>
</li>
</ul>
</div>
JS:
var app = angular.module('todoApp', []);
app.controller('TodoListController', ['$scope', function ($scope) {
$scope.todos = [];
$scope.newField = [];
$scope.customStyle = {};
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false, editing: false});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.delete = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
$scope.remove = function () {
$scope.todos.splice(this.$index, 1);
};
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
};
$scope.save = function (index) {
$scope.todos[index].editing = false;
};
$scope.cancel = function (index) {
$scope.todos[index] = $scope.newField[index];
};
$scope.updateVar = function (event) {
$scope.myVar = angular.element(event.target).text();
};
$scope.editKeyword = function (name, index) {
$scope.mode[index] = 'edit';
console.log(name);
};
}]);
Just add a todoDate attribute in the todo model like
$scope.todos.push({text: $scope.todoText, done: false, editing: false, todoDate: new Date()});
and update it when the user updates the todo object ie save()
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].todoDate = new Date();
};
Hope it helps.
When you call save function to add new todos simply add the createdOn field.
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].createdOn = new Date().getTime();
};
Now when user edits the todo and change function is called, do as below
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
$scope.todos[todoIndex].LastModifyOn = new Date().getTime();
};
Now if this updated todo is again edited by the user we need to simply call change function and it will update the LastModifyOn filed.
So by doing this, we can keep both data like when was the todo created and when was it last gets updated.
var app = angular.module('todoApp', []);
app.controller('TodoListController', ['$scope', function ($scope) {
$scope.todos = [];
$scope.newField = [];
$scope.customStyle = {};
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false, editing: false, created: new Date()});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.delete = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
$scope.remove = function () {
$scope.todos.splice(this.$index, 1);
};
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
$scope.todos[todoIndex].created = new Date();
};
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].created = new Date();
};
$scope.cancel = function (index) {
$scope.todos[index] = $scope.newField[index];
};
$scope.updateVar = function (event) {
$scope.myVar = angular.element(event.target).text();
};
$scope.editKeyword = function (name, index) {
$scope.mode[index] = 'edit';
console.log(name);
};
}]);
I am trying to preopulate a knockout component on document ready.
I've written the following code:
function Finding(id, trigger) {
var self = this;
self.id = ko.observable(id);
self.trigger = ko.observable(trigger);
}
function FindingViewModel() {
let self = this;
self.findings = ko.observableArray();
self.addFinding = function () {
self.findings.push(new Finding(self.findings().length + 1, ""));
};
self.removeFinding = function (finding) {
self.findings.remove(finding);
ko.utils.arrayForEach(self.findings(), function (value, i) {
self.findings.replace(value, new Finding(i + 1, value.trigger()));
});
};
self.update = function (data) {
var findings = data.findings;
for (var index = 0; index < findings.length; ++index) {
var finding = findings[index];
self.findings.push(new Finding(self.findings().length + 1, finding.trigger));
}
};
}
ko.components.register('finding', {
template: `<table>
<tbody data-bind="foreach: findings">
<tr>
<td><span data-bind="text: id"/></td>
<td><input data-bind="value: trigger"/></td>
<td>Remove</td>
</tr></tbody></table>
<button data-bind="click: addFinding">Add a Finding</button>`,
viewModel: FindingViewModel
});
$(function () {
ko.applyBindings();
$.getJSON("/_get_findings", function (data) {
//findingModel.update(data);
})
});
How can I access the underlying Viewmodel from the finding component to set data from inside the getJSON function?
thx to Jeroen the solution looks like this:
function FindingViewModel() {
let self = this;
self.findings = ko.observableArray();
self.addFinding = function () {
self.findings.push(new Finding(self.findings().length + 1, ""));
};
self.removeFinding = function (finding) {
self.findings.remove(finding);
ko.utils.arrayForEach(self.findings(), function (value, i) {
self.findings.replace(value, new Finding(i + 1, value.trigger()));
});
};
self.update = function (data) {
let findings = data.findings;
for (let index = 0; index < findings.length; ++index) {
let finding = findings[index];
self.findings.push(new Finding(self.findings().length + 1, finding.trigger));
}
};
$.getJSON("/_get_findings", function (data) {
self.update(data);
});
}
Why does my click event not work in the second foreach?
My html:
<div class="row" id="menuBody" data-bind="foreach:categoryArray">
<div class="outer col-md-2" data-bind=" attr:{id:id},event:{mouseover :$parent.mouseOverKnockout,mouseout:$parent.mouseOutKnockout }">
<div class="inner col-md-12">
<label class="label" data-bind="text:name"> </label>
<div class="children" data-bind="css:{changeTopChildren: changeTopChildren}">
<ul data-bind="foreach:$parent.items1" class="childrenUl">
<li data-bind=" text: name,attr:{id:id},click: $parent.selectLi" class="childrenLi col-md-12"></li>
</ul>
</div>
</div>
</div>
</div>
My script:
var modelCategory = function (id, name) {
var self = this;
self.changeTopChildren = ko.observable(false);
self.name = ko.observable(name);
self.id = ko.observable(id);
}
var modelProduct = function (id, name) {
var _self = this;
_self.name = ko.observable(name);
_self.id = ko.observable(id);
_self.selectLi = function () {
alert("li");
console.log(" selectLi");
};
}
var viewModelMenuBody = function () {
var self = this;
self.selectLi = function (tag) {
alert("li");
console.log(" selectLi");
};
self.categoryArray = ko.observableArray();
self.items1 = ko.observableArray();
var temp = null;
self.mouseOverKnockout = function (arg, e) {
temp = arg;
for (var i = 0; i < self.categoryArray().length; i++) {
self.categoryArray()[i].changeTopChildren(false);
}
arg.changeTopChildren(true);
$.getJSON("/Home/getChildrenForMenu", { id: arg.id }, function (rowProduct) {
self.items1.removeAll();
for (var total = 0; total < rowProduct.length; total++) {
var temp = new modelProduct();
temp.id(rowProduct[total].id);
temp.name(rowProduct[total].name);
self.items1.push(temp);
}
});
}
self.mouseOutKnockout = function (arg) {
if (arg!=null)
arg.changeTopChildren(false);
//else
// temp.changeTopChildren(false);
};
(function () {
$.getJSON("/Home/getDataForMenu", null, function (rowCategory) {
for (var total = 0; total < rowCategory.length; total++) {
var temp = new modelCategory();
temp.id(rowCategory[total].id);
temp.name(rowCategory[total].name);
self.categoryArray.push(temp);
}
});
})();
};
var viewModel1 = new viewModelMenuBody();
ko.applyBindings(viewModel1, document.getElementById('menuBody'));
All methods are defined within root viewmodel object. So $parent.selectLi call from the nested foreach binding won't work because $parent context refers to current item of the outer foreach.
Use $root.selectLi instead.
More info: http://knockoutjs.com/documentation/binding-context.html
I have an observableArray which is displayed in a table using foreach binding where values are displayed inside textboxes. Now what I want to do is to add an edit link on each row which enables/disables the readonly state of the corresponding textbox in its row. I can do it, but the way I did it messed up my add new line (push) functionality.
Here is a fiddle of my code.
Try to delete a line then add it again by selecting it in the dropdown list, the edit link disappears as well as the value.
Any help will be greatly appreciated! Thank you.
So here's my HTML:
<table class="input-group" >
<tbody data-bind="foreach: loanDeductions">
<tr>
<td><strong data-bind='text: deductionName'></strong></td>
<td><input class="deductionCode form-control" style="text-align: right" data-bind='value: amount, valueUpdate: "afterkeydown", attr: { "readonly": getreadonlyState() }' /></td>
<td><a href='#' data-bind='click: $parent.removeLine'>Delete</a></td>
<td><span data-bind="text: linkText"></span></td>
</tr>
</tbody>
</table>
<table>
<tr>
<td colspan="3"><select data-bind="options: loanDeductionsList(), optionsText: 'deductionName', optionsCaption: 'Choose a deduction..', value: selectedDeduction"></select></td>
</tr>
</table>
Now here is my script:
var deductionLine = function (deductionID, deductionName, amount) {
self = this;
self.deductionID = ko.observable(deductionID);
self.deductionName = ko.observable(deductionName);
self.amount = ko.observable(formatCurrency(amount));
self.getreadonlyState = ko.observable('readonly');
self.linkText = ko.computed(function () {
return this.getreadonlyState() == 'readonly' ? "Edit" : "Stop Edit";
}, self);
};
var deductionList = function (deductionID, deductionName, amount) {
self = this;
self.deductionID = ko.observable(deductionID);
self.deductionName = ko.observable(deductionName);
self.amount = ko.observable(formatCurrency(amount));
};
function LoanDeductions(deductions) {
var self = this;
self.loanDeductions = ko.observableArray(ko.utils.arrayMap(deductions, function (deduction) {
return new deductionLine(deduction.deductionID, deduction.deductionName, deduction.amount)
}));
self.loanDeductionsList = ko.observableArray(ko.utils.arrayMap(deductions, function (deduction) {
return new deductionList(deduction.deductionID, deduction.deductionName, deduction.amount)
}));
self.selectedDeduction = ko.observable();
//edit link
self.readonly = function () {
if (BossBaU) {
if (this.getreadonlyState()) {
this.getreadonlyState(undefined);
}
else {
this.getreadonlyState('readonly');
}
}
else alert('Access denied!');
}
// adds deduction
self.selectedDeduction.subscribe(function (data) {
var match = ko.utils.arrayFirst(self.loanDeductions(), function (deduction) {
return deduction.deductionID() === data.deductionID();
});
if (match) {
alert(data.deductionName() + ' already exists!');
self.showAddDeduc(false);
} else {
self.loanDeductions.push({
deductionID: data.deductionID,
deductionName: data.deductionName,
amount: data.amount,
});
self.showAddDeduc(false);
}
});
//delete deduction
self.removeLine = function (line) { self.loanDeductions.remove(line) };
};
var viewModel = new LoanDeductions(#Html.Raw(Model.CRefLoansDeductions2.ToJson()));
$(document).ready(function () {
ko.applyBindings(viewModel);
});
In the subscribe handler, self.selectedDeduction.subscribe, you're adding an object to the list of loanDeductions when you should be adding a new instance of deductionLine just as you do when you declare self.loanDeductions.
Or to put it another way, self.loadDeductions is an observableArray of deductionLine instances, to which you then add an object with three properties.
Change that subscribe handler to push a new deductionLine(...) and you'll see the difference.
I found the cause of the problem, I had to mirror every changes I made with my observableArray to my list.
var deductionLine = function (deductionID, deductionName, amount) {
self = this;
self.deductionID = ko.observable(deductionID);
self.deductionName = ko.observable(deductionName);
self.amount = ko.observable(amount);
self.getreadonlyState = ko.observable('readonly');
self.linkText = ko.computed(function () {
return this.getreadonlyState() == 'readonly' ? "Edit" : "Stop Edit";
}, self);
};
var deductionList = function (deductionID, deductionName, amount) {
self = this;
self.deductionID = ko.observable(deductionID);
self.deductionName = ko.observable(deductionName);
self.amount = ko.observable(amount);
self.getreadonlyState = ko.observable('readonly');
self.linkText = ko.computed(function () {
return this.getreadonlyState() == 'readonly' ? "Edit" : "Stop Edit";
}, self);
};
Here's the fiddle in case anyone bump into a similar issue.
JSFiddle.
I am trying to push objects for an existing array into a knockout observable array but am failing terrible. How do I get this code to work, and since this code sucks what is the 'proper' way of doing this. Thank you so much
<div data-bind="foreach: Bob">
<div data-bind="text: $data">
</div>
</div>
var Test = function(){
var self= this;
self.Init = function(name){
self.Name = ko.observable(name);
};
};
var ArrayOfTests = function() {
var self = this;
self.Init = function(tests){
self.AllTests = ko.observableArray([]);
for(var i = 0; i <=tests.length-1;i++)
{
self.AllTests.push(tests[i]);
};
};
};
var TestViewModule = function(){
self.Bob = ko.observable();
var temp = new ArrayOfTests();
var tempTest = new Test();
tempTest.Init('one2');
temp.Init([tempTest,tempTest]);
self.Bob= temp;
};
ko.applyBindings(new TestViewModule());
console.log(new TestViewModule());
The answer ended up being, I was adding a null item to the array making the array not work.
You pass 'one2' string in Test.Init, but this method don't accept parameters:
var Test = function(){
var self= this;
self.Name = ko.observable();
self.Init = function(data){
self.Name(data);
};
};
Edit
I completly refactored your viewModel, because current implementation is not correct:
Html:
<div data-bind="with: Bob">
<div data-bind="foreach: AllTests">
<div data-bind="text: $data">
</div>
</div>
</div>
ViewModel:
var Test = function(){
var self= this;
self.Name = ko.observable(name);
self.Init = function(name){
self.Name(name);
};
};
var ArrayOfTests = function() {
var self = this;
self.AllTests = ko.observableArray();
self.Init = function(tests) {
for(var i = 0; i < tests.length; i++) {
self.AllTests.push(tests[i].Name());
};
};
};
var TestViewModule = function(){
var temp = new ArrayOfTests();
var tempTest = new Test();
tempTest.Init('one2');
temp.Init([tempTest,tempTest]);
self.Bob = ko.observable(temp);
};
ko.applyBindings(new TestViewModule());
console.log(new TestViewModule());