Knockout Js binding to an edit modal with an undo feature - javascript

I am using ko.js to display a table of venues.
Each venue has an edit button that brings up a dialog showing the editable data.
When the edit button is pressed I bind the venue to the dialog and I store a copy of the data in an undo object.
When I edit fields on the dialog, both the dialog and table are updated.
When I cancel the edit, I bind the venue to the undo objects state. This updates the dialog but it does not update on the table.
Any idea what I am doing wrong here?
Here is my view model.
VenueViewModel = function(venues) {
var self = this;
var venueModal = $("#venueModal");
this.venues = ko.mapping.fromJS(venues);
this.venue = ko.observable();
this.venueUndo = null;
//Cancel an edit
this.cancel = function() {
self.venue(ko.mapping.fromJS(self.venueUndo));
venueModal.modal("hide");
}
//Edit an existing venue
this.edit = function(venue) {
self.venue(venue);
self.venueUndo = ko.mapping.toJS(venue);
venueModal.modal("show");
};
//Create a new venue
this.create = function() {
self.venue(new Venue());
venueModal.modal("show");
};
};
ko.applyBindings(new VenueViewModel(venues));

The article nemesv linked in his comment was the answer.
http://www.knockmeout.net/2013/01/simple-editor-pattern-knockout-js.html

You might consider using KO-UndoManager for this. Here's a sample code to register your viewmodel:
VenueViewModel.undoMgr = ko.undoManager(VenueViewModel, {
levels: 12,
undoLabel: "Undo (#COUNT#)",
redoLabel: "Redo"
});
You can then add undo/redo buttons in your html as follows:
<div class="row center-block">
<button class="btn btn-primary" data-bind="
click: undoMgr.undoCommand.execute,
text: undoMgr.undoCommand.name,
css: { disabled: !undoMgr.undoCommand.enabled() }">UNDO</button>
<button class="btn btn-primary" data-bind="
click: undoMgr.redoCommand.execute,
text: undoMgr.redoCommand.name,
css: { disabled: !undoMgr.redoCommand.enabled() }">REDO</button>
</div>
And here's a Plunkr showing it in action.

You set Knockout observables like this:
self.venue(ko.mapping.fromJS(self.venueUndo));

We Have a small extension to Knockout.js as part out project that extends observables so they can be registered to different stacks of there history.
Maybe it can help you.
Knockout-Memento

Related

Value not changed back when canceled

This is in angular. There is a p-table that shows some data, like below:
When I clicked on the pencil icon to edit the table row, this pops up. I'll call this the edit dialog:
When I make any kind of edit in the text input box, the table row on p-table also change to the new value; example if I change the Download URL to "sss", the p-table also shows "sss" even though I haven't clicked the "Update" button yet. However, if I decide to cancel the change by clicking on the Cancel button, the new value ("sss") stays and the old value does not revert back. Any help on reverting it back is appreciated.
Here is how my code is currently set up: when I open the page the first time, it retrieves the data in the table from a database and display in the p-table. When I click on the pencil icon, it uses DynamicDialog to open the edit dialog that was created by another component.
Here's the typescript code to that it uses to open the edit dialog, which is called ModelDialogComponent:
this.ref = this.dialogService.open(ModelDialogComponent, {
data: {
record,
},
header: title,
width: '500px',
contentStyle: { 'max-height': '500px' },
baseZIndex: 10000,
dismissableMask: true,
})
ModelDialogComponent has five input text and two buttons. Each of the input text is create in this similar fashion. This is one of the html code for it:
<!--Applicationn Name-->
<div class="p-field p-grid" style="padding: 10px">
<label class="p-col-fixed" style="width: 150px">App Name </label>
<div class="p-col">
<input type="text" pInputText readonly [(ngModel)]="updateData.app" />
</div>
</div>
The Cancel button is tied to a function: (click) ="cancelUpdate()"
cancelUpdate() is this:
cancelUpdate(){
this.config.data.record = this.clonedVersion;
this.config.data.record = {...this.config.data.record};
this.updateData = this.clonedVersion;
this.updateData = {...this.updateData};
this.ref.close(this.clonedVersion);
}
Here is ModelDialogComponent init:
ngOnInit() {
this.updateData = this.config.data.record;
this.clonedVersion = {...this.updateData};
}
Canceling does not revert the value back to its original value, and how do I do that?
If you need more info, please let me know.
I figured out a way to get it to revert back. use onClose, and then get the original data from the database. This works good for now because the database is small. As it grows, I think I will need to figure out why cloning the table row and setting it back does not work. Here's my code for the solution I made:
this.ref.onClose.subscribe(() => {
this.messageService.add({
severity:'info',
summary: 'Canceled Editing',
detail:'User canceled edits',
sticky: false,
});
this.ToolVersionService.getAllVersion().subscribe((data) => {
this.rowData = data;
this.loading = false;
this.EditDetails = false;
},
(err) => {
console.error(err)
});
});

How to set the button in my toolbar to disabled

I have created a toolbar in my windows 10 UWP winjs app and I want to disable some of the buttons.
I append attributes to the button like so :
new WinJS.UI.Command(null, {
disable: true,
id: 'cmdSave',
label: 'save',
section: 'primary',
type: 'button',
icon: 'save',
onclick: clickbuttonprintout()
});
I have looked through the winjs css files and found many disabled tags. Is it possible to set the button to disabled like I have appended other attributes above ?
Figured this out :
You select the button, set it to disabled and then process it.
var thisBtn = document.getElementById('cmdSave');
thisBtn.disabled = true;
WinJS.UI.process(btn); //this is key
With this in mind, I set up a function so I can pass different buttons to it:
function disableButton(buttonID){
var btn = document.getElementById(buttonID);
btn.disabled = true;
WinJS.UI.process(btn);
}
P.S
Even though this is not part of the question, it may help people.
What about editing the attributes on the button too ? Ive made this function to edit any attribute on a winjs button :
function changeButtonAttributes(buttonId, element, attribute) {
var btn = document.getElementById(buttonId); //select button
btn.winControl[element] = attribute; //button.element = attribute
WinJS.UI.process(btn); //process all
}
Hope that helps :)

Better way to displaying data from view model using knockoutjs?

I have a view model with a separate hierarchy and during, say a click event, I'd like to display a modal dialog of the data from the secondary hierarchy for the "clicked" data item. To make this a little easier to follow, I have mocked up an example in jsfiddle that achieves the desired result (without a modal for simplicity), but it's done by repeating the same markup instead of modifying the data from a single group of markup.
var FoundingFathersViewModel = function(data) {
var self = this;
self.foundingFathers = ko.observableArray([]);
//click
self.detail = function(father) {
//get the selected Founding Father's positions HTML and
//set the HTML of the detail div
var html = $('#'+father.id()).html();
$('#detail').html(html);
};
var mapping = $.map(data, function(item) { return new FoundingFather(item); });
self.foundingFathers(mapping);
};
var FoundingFather = function(item) {
this.id = ko.observable(item.id);
this.name = ko.observable(item.name);
this.positions = ko.observableArray(item.positions);
};
ko.applyBindings(new FoundingFathersViewModel(data));
The jsfiddle code simply modifies the CSS display property to display the correct detail. I'd like to think there would be a "cleaner" way to do this. Any help would be appreciated. And if there's not a more elegant solution, I'd like to know that too.
https://jsfiddle.net/jvz6gktm/2/
I'd suggest moving towards a selectedItem approach. Clicking on a president sets that one as the selected item, which is what populates the detail section.
see my updated fiddle:
https://jsfiddle.net/jvz6gktm/5/
detail area:
<div id="detail" data-bind="with: selectedPresident">
<div class="positions">
<div data-bind="foreach: positions" class="detail">
<h5 data-bind="html: position" />
<h6 data-bind="html: yearsActive" />
</div>
</div>
</div>
selection:
self.selectedPresident = ko.observable();
self.selectPresident = function(father) {
self.selectedPresident(father);
};
As a side note: if you're using knockout, don't use jQuery to go messing with the DOM, you're just asking for trouble.

How to add multiple items to context menu in Kendo Scheduler?

"kendoContextMenu" is one of control from Telerik suit. I am trying to attach it with Kendo Scheduler control.
Below is the code to render scheduler and menu
Part of it taken from Kendo sample site
<div id="example">
<div id="scheduler"></div>
<ul id="contextMenu"></ul>
</div>
Here is Context Menu Initialization
$("#contextMenu").kendoContextMenu({
filter: ".k-event, .k-scheduler-table td",
target: "#scheduler",
select: function(e) {
var target = $(e.target);
if (target.hasClass("k-event")) {
var occurrenceByUid = scheduler.occurrenceByUid(target.data("uid"));
} else {
var slot = scheduler.slotByElement(target);
}
},
open: function(e) {
var menu = e.sender;
var text = $(e.target).hasClass("k-event") ? "Edit Title" : "Block";
menu.remove(".myClass");
menu.append([{text: text, cssClass: "myClass" }]);
}
});
});
The above code adds only ONE item in context menu and click event directly fires up. I would like to have multiple items in a context menu and each should have its own event so that I can use them as it clicked.
Below image shows right click behavior, where it shows only Block in a menu
I am trying to get menu as below- which has multiple items and have its own click events
I am trying like below by appending text but it's seems to be wrong way to do and it can not have separate click event.
open: function(e) {
var menu = e.sender;
var text = $(e.target).hasClass("k-event") ? "Edit event" : "Add Event";
text = text + "|" + "Cancel"
menu.remove(".myClass");
menu.append([{text: text, cssClass: "myClass" }]);
}
Kindly help
I'm afraid you're appending it wrong. By concatenating "| Cancel" you're not adding a new item, but adding text to the existing one.
Try creating a new object and append it with append():
menu.append([{text: "Cancel", cssClass: "cancel-opt" }]);
Then you check by the class inside the select event:
if (target.hasClass("cancel-opt"))

Simple ToDo App using Agility.js with Persistence

I created a database called agilityjs that has a table called todolist. I created a file in /api/todolist.php which will act as a web service. I want to make it so when the user clicks "New Item," it will add a new item to my database by calling the PHP file (todolist.php). My next goal is to edit the item when they click to edit and delete when they click to delete.
Can anyone help me in the right direction? I have seen the persistence documentation at http://agilityjs.com/docs/docs.html#persist but I'm not sure how to apply it.
The following code is simply taken from http://agilityjs.com/ (at the bottom, you can see it and it's live demo).
Here is another good resource for learning https://gist.github.com/3166678, but I can't seem to apply it either.
//
// Item prototype
//
var item = $$({}, '<li><span data-bind="content"/> <button>x</button></li>', '& span { cursor:pointer; }', {
'click span': function(){
var input = prompt('Edit to-do item:', this.model.get('content'));
if (!input) return;
this.model.set({content:input});
},
'click button': function(){
this.destroy();
}
});
//
// List of items
//
var list = $$({}, '<div> <button id="new">New item</button> <ul></ul> </div>', {
'click #new': function(){
var newItem = $$(item, {content:'Click to edit'});
this.append(newItem, 'ul'); // add to container, appending at <ul>
}
});
$$.document.append(list);
Thank you.

Categories

Resources