Feb 24 2015

Comment by Seth Spearman on How do you get a Knockout observable array to work with a jQuery dialog

Ryan also referenced the pattern for doing this edit/cancel scenario here... knockmeout.net/2013/01/simple-editor-pattern-knockout-js.htm‌​l
Feb 24 2015

Comment by Seth Spearman on How do you get a Knockout observable array to work with a jQuery dialog

Ryan also referenced the pattern for doing this edit/cancel scenario here... knockmeout.net/2013/01/simple-editor-pattern-knockout-js.html
Feb 24 2015

Answer by Seth Spearman for How do you get a Knockout observable array to work with a jQuery dialog

Changing this...

if (vm.currentItemIsNew()) {       
   vm.DiscountCodes.push({
        Id: vm.currentId,
        Description: vm.currentDescription,
        Code: vm.currentCode,
        DiscountAmount: vm.currentDiscountAmount,
        MaxUsages: vm.currentMaxUsages,
        NumberOfUsages: vm.currentNumberOfUsages,
        Active: vm.currentActive
    });

to this...

if (vm.currentItemIsNew()) {
    vm.DiscountCodes.push(new DiscountCode(
        vm.currentDescription(),
        vm.currentCode(),
        vm.currentDiscountAmount(),
        vm.currentMaxUsages(),
        vm.currentNumberOfUsages(),
        vm.currentActive()
    ));

...did the trick.
HT to Ryan Niemeyer (knockmeout.com) for the solution

And to see a greatly simplified version...

http://jsfiddle.net/gh6mzrxp/34/

Feb 24 2015

Answer by Seth Spearman for How do you get a Knockout observable array to work with a jQuery dialog

Changing this...
if (vm.currentItemIsNew()) {       
   vm.DiscountCodes.push({
        Id: vm.currentId,
        Description: vm.currentDescription,
        Code: vm.currentCode,
        DiscountAmount: vm.currentDiscountAmount,
        MaxUsages: vm.currentMaxUsages,
        NumberOfUsages: vm.currentNumberOfUsages,
        Active: vm.currentActive
    });
to this...
if (vm.currentItemIsNew()) {
    vm.DiscountCodes.push(new DiscountCode(
        vm.currentDescription(),
        vm.currentCode(),
        vm.currentDiscountAmount(),
        vm.currentMaxUsages(),
        vm.currentNumberOfUsages(),
        vm.currentActive()
    ));
...did the trick.
HT to Ryan Niemeyer (knockmeout.com) for the solution And to see a greatly simplified version... http://jsfiddle.net/gh6mzrxp/34/
Feb 23 2015

Answer by Seth Spearman for Can Orchard 1.8.x be installed on a build server that does not have Visual Studio Installed?

Here is my answer...copied and pasted from the forum.

This is a late answer. Bottom line is that I did eventually get our build server to build orchard projects. I am not sure if this is a complete answer or not as I should have answered as soon as I had it done. But I pretty sure this will work.

One other thing to note...It is quite possible that not all of these steps are required. I tried a lot of different things and it might be in the end some of the steps were not needed. It might also reflect a problem on our build server...especially the fact that I had to manually add registry entries.

But here it is without details.

Install the .NET Framework SDK for Windows 7.1. http://www.microsoft.com/en-us/download/details.aspx?id=8279

Install the .NET 4.5 Full http://www.microsoft.com/en-us/download/details.aspx?id=30653

Install Visual Studio 2013 Express for Web http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx

Install the Visual Studio 2013 Build Tools http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx

Finally, even after that...it didn't work until I added the following registry keys. I would attach the .reg file but I don't see how. Save the lines between the lines into a fixBuild.reg file. Then double click...


Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VC7]
"FrameworkDir32"="C:\Windows\Microsoft.NET\Framework\"
"FrameworkDir64"="C:\Windows\Microsoft.NET\Framework64"
"11.0"="C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\"
"FrameworkVer32"="v4.0.30319"
"FrameworkVer64"="v4.0.30319"
"12.0"="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\"


Finally, to actually do the build, I created a batch file that does the build.
Save the lines between the lines into a doBuild.cmd file. Be sure to place this in the root of the source download...then double click to build...


call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat"
msbuild /t:FastBuild Orchard.proj
The first line gets your environment ready. The second build the project using the FastBuild target in the Orchard.proj file. You can read that file if you want to try other build targets.

******* EDIT
There is one thing I forgot to mention. And that is that one thing you have to do to get this to work is that you have to edit some CSPROJ files because the command line build fails with errors like these...

\Orchard\src\Orchard.Web\Modules\CKEditor\CKEditor.csproj(337,6): error : "None" element name for include "Placement.info" should be "Content". \Orchard\src\Orchard.Web\Modules\Orchard.Tokens\Tests\Orchard.Tokens.Tests.csproj(82,6): error : "None" element name for include "app.config" sh \Orchard\src\Orchard.Web\Modules\TinyMceDeluxe\TinyMceDeluxe.csproj(377,6): error : "None" element name for include "app.config" should be "Cont \Orchard\src\Orchard.Web\Modules\Upgrade\Upgrade.csproj(156,6): error : "None" element name for include "app.config" should be "Content". [C:\Us \Orchard\src\Orchard.Web\Themes\Themes.csproj(280,6): error : "None" element name for include "Upward\Views\Content-Story.Detail.cshtml" should

The fix is to do exactly what the error indicates. Open the csproj files and search/replace "

Also, some of the projects reference app.config files that do not exist in source. You also have to remove those references. I usually just delete the content node entirely or the group entirely.

Seth

Feb 23 2015

Answer by Seth Spearman for Can Orchard 1.8.x be installed on a build server that does not have Visual Studio Installed?

Here is my answer...copied and pasted from the forum. This is a late answer. Bottom line is that I did eventually get our build server to build orchard projects. I am not sure if this is a complete answer or not as I should have answered as soon as I had it done. But I pretty sure this will work. One other thing to note...It is quite possible that not all of these steps are required. I tried a lot of different things and it might be in the end some of the steps were not needed. It might also reflect a problem on our build server...especially the fact that I had to manually add registry entries. But here it is without details. Install the .NET Framework SDK for Windows 7.1. http://www.microsoft.com/en-us/download/details.aspx?id=8279 Install the .NET 4.5 Full http://www.microsoft.com/en-us/download/details.aspx?id=30653 Install Visual Studio 2013 Express for Web http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx Install the Visual Studio 2013 Build Tools http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx Finally, even after that...it didn't work until I added the following registry keys. I would attach the .reg file but I don't see how. Save the lines between the lines into a fixBuild.reg file. Then double click...
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftVisualStudioSxSVC7]
"FrameworkDir32"="C:WindowsMicrosoft.NETFramework"
"FrameworkDir64"="C:WindowsMicrosoft.NETFramework64"
"11.0"="C:Program Files (x86)Microsoft Visual Studio 11.0VC"
"FrameworkVer32"="v4.0.30319"
"FrameworkVer64"="v4.0.30319"
"12.0"="C:Program Files (x86)Microsoft Visual Studio 12.0VC"
Finally, to actually do the build, I created a batch file that does the build.
Save the lines between the lines into a doBuild.cmd file. Be sure to place this in the root of the source download...then double click to build...

call "C:Program Files (x86)Microsoft Visual Studio 12.0Common7ToolsVsDevCmd.bat"
msbuild /t:FastBuild Orchard.proj
The first line gets your environment ready. The second build the project using the FastBuild target in the Orchard.proj file. You can read that file if you want to try other build targets. ******* EDIT
There is one thing I forgot to mention. And that is that one thing you have to do to get this to work is that you have to edit some CSPROJ files because the command line build fails with errors like these... OrchardsrcOrchard.WebModulesCKEditorCKEditor.csproj(337,6): error : "None" element name for include "Placement.info" should be "Content". OrchardsrcOrchard.WebModulesOrchard.TokensTestsOrchard.Tokens.Tests.csproj(82,6): error : "None" element name for include "app.config" sh OrchardsrcOrchard.WebModulesTinyMceDeluxeTinyMceDeluxe.csproj(377,6): error : "None" element name for include "app.config" should be "Cont OrchardsrcOrchard.WebModulesUpgradeUpgrade.csproj(156,6): error : "None" element name for include "app.config" should be "Content". [C:Us OrchardsrcOrchard.WebThemesThemes.csproj(280,6): error : "None" element name for include "UpwardViewsContent-Story.Detail.cshtml" should The fix is to do exactly what the error indicates. Open the csproj files and search/replace " Also, some of the projects reference app.config files that do not exist in source. You also have to remove those references. I usually just delete the content node entirely or the group entirely. Seth
Feb 23 2015

How do you get a Knockout observable array to work with a jQuery dialog

I am new to javascript and knockout. Last week I spent all day trying to get a knockout observable to work with a jquery dialog for editing and adding new values to the array of items. I got SO close but in the end could not get it to work exactly.

My question has two parts.

I have created a JSFiddle that perfectly re-creates my problem.

This fiddle demonstrates that I can edit an existing discount code just fine.

I can also add a discount code. However when I add or edit a second (or third, etc) NEW discount code ALL of the NEW ones get edited. It does add the new one but the first NEW one gets the same values as the second new item.

Here is the fiddle...

http://jsfiddle.net/sethspearman/2jxtpw7s/28/

First Question. Can you get this fiddle to work? I think the fix could be simple. What do I need to change.

Second Question. I know that this fiddle example is NOT making best usage of knockout bindings.

For example, I am setting and then adding or editing using a vm.CurrentDiscountCode observable. However, I know that knockout has a $data binding context that could and should be used instead. I could never figure out how to get it to work. I figured out how to EDIT existing items using the $data context but could not figure out how to add new ones.

So to sum it up.
What is the BARE minimum of change to get the fiddle working? And what is the OPTIMAL way to get this working?

HERE IS THE CODE from the fiddle.

EDIT Fiddle is now working. Did not work when using HTTPS.

HTML

<body>
    <h1>Discount Codes Test</h1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-debug.js"></script>
<script id="editTmpl" type="text/html">
    <div>
        <ul>
            <li>
                <div class="inline-item">
                    <input type="hidden" name="codeId" id="codeId" data-bind="value: $parent.currentId" />
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Title</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="description" id="description" data-bind="value: $parent.currentDescription" />
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Code</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="discountCode" id="discountCode" data-bind="value: $root.currentCode, keypressvalidator: $root.currentCode, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentCode, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Discount Amount</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="amount" id="amount" data-bind="value: $root.currentDiscountAmount, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentDiscountAmount, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Max Usages</span>
                </div>
                <div class="inline-item">
                    <input type="text" name="maxUsages" id="maxUsages" data-bind="value: $root.currentMaxUsages, visible: $parent.currentItemIsNew() == true" />
                    <label data-bind="text: $parent.currentMaxUsages, visible: $parent.currentItemIsNew() == false"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Number of Usages</span>
                </div>
                <div class="inline-item">
                    <label data-bind="text: $parent.currentNumberOfUsages"></label>
                </div>
            </li>
            <li>
                <div class="inline-item w4 right-text">
                    <span class="label auto">Active</span>
                </div>
                <div class="inline-item">
                    <input type="checkbox" name="isActive" id="isActive" data-bind="checked: $root.currentActive" />
                </div>
            </li>
        </ul>

        <button data-bind="jqButton: {}, click:$root.accept">Accept</button>
        <button data-bind="jqButton: {}, click:$root.cancel">Cancel</button>
    </div>
</script>    


<!--
**************************************
-->   
        <div>
            <fieldset data-regmode="printed">
                <div>
                    <h3>Add or Edit Discount Codes</h3>
                    <div id="discountCodes"></div>
                    <table>
                        <thead>
                            <tr>
                                <th>Title</th>
                                <th>Code</th>
                                <th>DiscountAmount</th>
                                <th>MaxUsages</th>
                                <th>Active</th>
                                <th>&nbsp;</th>
                            </tr>
                        </thead>
                        <tbody data-bind="foreach: DiscountCodes">
                            <tr style="cursor: pointer">
                                <td data-bind="text: Description"></td>
                                <td data-bind="text: Code"></td>
                                <td data-bind="text: DiscountAmount"></td>
                                <td data-bind="text: MaxUsages"></td>
                                <td data-bind="text: NumberOfUsages"></td>
                                <td data-bind="text: Active"></td>
                                <td><button type="button" data-bind="click: $root.editDiscountCode">Edit</button></td>
                            </tr>
                        </tbody>
                        <tfoot>
                            <tr> 
                                <td colspan="6"><button type="button" id="create-code" data-bind="click: createDiscountCode">Add New</button></td>
                            </tr>
                        </tfoot>
                    </table>
                </div>  
            </fieldset>
        </div>
        <div id="details" data-bind="jqDialog: { autoOpen: false, resizable: false, modal: true, height: 400, width:350, title:'Add/Edit Discount Code' }, template: { name: 'editTmpl', data: currentDiscountCode, if: currentDiscountCode }, openDialog: currentDiscountCode"></div>


</body>

JAVASCRIPT

//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).dialog("destroy");
        });

        //dialog is moved to the bottom of the page by jQuery UI. Prevent initial pass of ko.applyBindings from hitting it
        setTimeout(function () {
            $(element).dialog(options);
        }, 0);
    }
};

var currentDialog;
//custom binding handler that opens/closes the dialog
ko.bindingHandlers.openDialog = {
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (typeof value == 'undefined') {
            return;
        }
        currentDialog = $(element);
        if (value) {
            $(element).dialog("open");
        } else {
            $(element).dialog("close");
        }
    }
};

//custom binding to initialize a jQuery UI button
ko.bindingHandlers.jqButton = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).button("destroy");
        });

        $(element).button(options);
    }
};

var vm = {};
var DiscountCode = function (description, code, discountAmount, maxUsages, numberOfUsages, active) {
    this.Id = ko.observable(0);
    this.Description = ko.observable(description)
    this.Code = ko.observable(code);
    this.DiscountAmount = ko.observable(discountAmount);
    this.MaxUsages = ko.observable(maxUsages);
    this.NumberOfUsages = ko.observable(numberOfUsages);
    this.Active = ko.observable(active);
}

var code1 = new DiscountCode('Give a 25% discount','DISC25',25, 5, 0, true); 
var code2 = new DiscountCode('Give a 10% discount','DISC10',10,20, 0, true); 
var code3 = new DiscountCode('Give a 5% discount' ,'DISC05', 5,30, 0, true); 

var discountCodes = ko.observableArray([code1,code2,code3]);

vm.DiscountCodes = discountCodes;

vm.currentId = ko.observable();
vm.currentDescription = ko.observable();
vm.currentCode = ko.observable().extend({ codeConverter: 0 });
vm.currentDiscountAmount = ko.observable();
vm.currentMaxUsages = ko.observable();
vm.currentNumberOfUsages = ko.observable();
vm.currentActive = ko.observable();

vm.currentDiscountCode = ko.observable();
vm.revertableDiscountCode = ko.observable();
vm.currentItemIsNew = ko.observable(false);

vm.editDiscountCode = function (discountCodeToEdit) {
    vm.revertableDiscountCode(discountCodeToEdit);
    vm.currentDiscountCode(discountCodeToEdit);
    vm.currentItemIsNew(false);
    setCurrent(discountCodeToEdit);
};

vm.createDiscountCode = function() {
    vm.currentDiscountCode(new DiscountCode("", "", 0, 0, 0, true));
    vm.currentItemIsNew(true);
    setCurrent(vm.currentDiscountCode());
};

function setCurrent(discountCode) {
    vm.currentId(discountCode.Id());
    vm.currentDescription (discountCode.Description());
    vm.currentCode(discountCode.Code());
    vm.currentDiscountAmount ( discountCode.DiscountAmount());
    vm.currentMaxUsages(discountCode.MaxUsages());
    vm.currentNumberOfUsages(discountCode.NumberOfUsages());
    vm.currentActive  (discountCode.Active());
}

vm.removeDiscountCode = function(discountCodeToRemove) {
    vm.DiscountCodes.remove(discountCodeToRemove);
}

vm.accept = function() {
    var currentItem = vm.currentDiscountCode();

    if (vm.currentItemIsNew()) {
        vm.DiscountCodes.push({
            Id: vm.currentId,
            Description: vm.currentDescription,
            Code: vm.currentCode,
            DiscountAmount: vm.currentDiscountAmount,
            MaxUsages: vm.currentMaxUsages,
            NumberOfUsages: vm.currentNumberOfUsages,
            Active: vm.currentActive
        });
        vm.currentItemIsNew(false);
    } else {
        currentItem.Id(vm.currentId());
        currentItem.Description(vm.currentDescription());
        currentItem.Code(vm.currentCode());
        currentItem.DiscountAmount(vm.currentDiscountAmount());
        currentItem.MaxUsages(vm.currentMaxUsages());
        currentItem.NumberOfUsages(vm.currentNumberOfUsages());
        currentItem.Active(vm.currentActive());
    }

    vm.currentDiscountCode("");
}

vm.cancel = function() {
    vm.currentDiscountCode(vm.revertableDiscountCode);
    currentDialog.dialog("close");
    vm.currentDiscountCode("");
}

ko.applyBindings(vm);

CSS

body {
    font-size: 62.5%;
}

label, input {
    display: block;
}

    input.text {
        margin-bottom: 12px;
        width: 95%;
        padding: .4em;
    }

fieldset {
    padding: 0;
    border: 0;
    margin-top: 25px;
}

h1 {
    font-size: 1.2em;
    margin: .6em 0;
}

div#users-contain {
    width: 350px;
    margin: 20px 0;
}

    div#users-contain table {
        margin: 1em 0;
        border-collapse: collapse;
        width: 100%;
    }

        div#users-contain table td, div#users-contain table th {
            border: 1px solid #eee;
            padding: .6em 10px;
            text-align: left;
        }

.ui-dialog .ui-state-error {
    padding: .3em;
}

.validateTips {
    border: 1px solid transparent;
    padding: 0.3em;
}

Thanks in advance for your help.

Seth

Feb 23 2015

How do you get a Knockout observable array to work with a jQuery dialog