0

UPDATED: See jsfiddle link below

UPDATED Again - See Update 2

I have the following HTML:

<button type="button" data-who="Appellant" data-bind="click: showLetter, hasFlag: { value: DeterminationLettersGenerated, flag: Enum_AppealParties.Appellee, enableIfTrue: true }">View</button>

Within the showLetter function I would like to do something like this:

self.showLetter = function (model, event) {
  var flagValue = $(event.target).data("bind").flag;
  ...
}

And by sibling, I mean siblings to the actual click event that is bound. I just need to get whatever will get me Enum_AppealParties.Appellee.

I have tried numerous combinations of ko.toJS, ko.toJSON, $.parseJSON and JSON.stringify. They always return me a string of the following with quotes or escaped quotes around it:

click: showLetter, hasFlag: { value: DeterminationLettersGenerated, flag: Enum_AppealParties.Appellee, enableIfTrue: true }

What I NEED is the above string converted to JSON so at worst I would need to do the following in code:

self.showLetter = function (model, event) {
  var magicObject = SomeAwesomeAnserHere();
  var flagValue = magicValue.hasFlag.flag;
  ...
}

UPDATE: Re the request to see a repo of it, check out this link Fiddle Just click on the View button within and some Alert messages will appear. The one that says "Should say Object" says it is a string. Not sure if the combinations I mention above are the way to go or what. Just want to get to each piece of the data-bind elements.

UPDATE 2: I know KO has to be doing what I am trying to accomplish, right? So after some digging around in the KO code, I see where it is turning the data-bind string into a usable object (in this case a function.) I am close to getting it to be useful within my own bindings/functions. This does not work 100% yet. But perhaps with someone smarter than me tinkering with it...

This code is within a KO.click event like the self.showLetter above:

var rewrittenBindings = ko.expressionRewriting.preProcessBindings($(event.target).data("bind"), null);
var functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
var almost = new Function("$context", "$element", functionBody);
Grandizer
  • 2,819
  • 4
  • 46
  • 75
  • 2
    Could you try to make a [repro](http://sscce.org)? I'm pretty sure we can help, but it's kinda hard without the entire setup. Ideally, give the Stack Snippets button in the question editor a spin. – Jeroen Oct 14 '14 at 20:40
  • It feels like you are going about this the wrong way and not using knockout the way it's designed to work. Why isn't the property you want part of the ViewModel itself? It doesn't really make a lot of sense to try and pull it back out of the binding except in a few cases (usually relating to custom bindings). – Matt Burland Oct 15 '14 at 13:58
  • @MattBurland the Enum_AppealParties is a global json object. It does not need nor make sense to be part of the model. Mostly because it is used in numerous locations and in different models. – Grandizer Oct 15 '14 at 15:37
  • 1
    @Grandizer Matt is right: you shouldn't have a global JSON object, per se. You should create the `enum` as a requirable module, and then require it into each viewModel that needs it. Or better yet, require it into your client-side data models. That's what we do. –  Oct 15 '14 at 15:48
  • @EricTaylor, that sounds interesting. Can you show a repo of what you mean? Not sure how to "require" things in javascript. And as a module to me would mean simply another file that get imported. Which to me, again seems like a global var anyway. Would love to see what you mean. May be a life changing day for my JS code. ;) – Grandizer Oct 16 '14 at 12:45
  • @Grandizer Please see the answer I posted here: http://stackoverflow.com/a/23595816/3174746. Let me know if that answer answers your question. Basically, you ENUMs will go into your `config` module. As your config grows, you might think about multiple config modules for the purposes of memory management and relevancy. –  Oct 16 '14 at 16:48

3 Answers3

2

To access sibling bindings, you need to define a custom binding. Defining such a binding that simply wraps the click binding is pretty simple:

ko.bindingHandlers.clickFlag = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        ko.applyBindingAccessorsToNode(element, {
            click: function() {
                return function(model, event) {
                    valueAccessor().call(this, model, event, allBindings.get('hasFlag'));
                }
            }
        }, bindingContext);
    }
}

http://jsfiddle.net/mbest/9mkw067h/85/

Michael Best
  • 16,623
  • 1
  • 37
  • 70
  • I've added an update to my at-time-of-writing accepted answer to point down to this... Grandizer, does this one not do exactly that you need without custom parsing code? – sifriday Oct 22 '14 at 10:29
1

Why not just append it to the click handler?

<button type="button" data-who="Appellant" data-bind="click: function() {showLetter($data,Enum_AppealParties.Appellee);}">View</button>

http://jsfiddle.net/9mkw067h/86/

I agree with the previous posters, though. This should be part of the model.

gliljas
  • 711
  • 8
  • 10
  • I like what you did there. My ``hasFlag`` does a bit more that was not in code as to enabling the button and such, but this is a great alternative if I do not have to do that. Thanks! – Grandizer Oct 16 '14 at 18:07
0

In this this similar-ish question, which ultimately fizzled out without a good answer:

Knockout how to get data-bind keys and value observables using element?

it became fairly clear the only way to access this info was via parsing the data-bind attribute. Here's an updated version of your fiddle showing how to parse a nested bind statement to get what you need:

http://jsfiddle.net/9mkw067h/83/

This is the code that does the parse:

self.showLetter = function (model, event) {
    var binding_info = {}
    var binding_attr = $(event.target).attr("data-bind")
    var indent = false, indent_key = "";
    $(binding_attr.split(",")).each(
        function(idx, binding) {
            var parts = binding.split(":")
            var key = parts[0].trim()
            var val = parts[1].trim()
            if (val.indexOf("{") != -1) {
                binding_info[key] = {}
                indent = true
                indent_key = key
            }
            if (indent == true) {
                binding_info[indent_key][key] = val.replace("{", "").replace("}", "").trim()
            }
            else {
                binding_info[key] = val
            }
            if (val.indexOf("}") != -1) {
                indent = false
                indent_key = ""
            }
        }
    )    
    console.log(binding_info.hasFlag.flag)
}

At the end of that, binding_info has what you're after.

Update:

The linked question above is slightly different, in that it starts from another view model and a given DOM element, and it says, can I get the bindings for that DOM element? It rules out a custom binding. However, in this instance, custom bindings are already in use, so Michael Best's post below provides a neater answer without custom parsing code and proves my assertion incorrect that custom parsing is the only way to do it!

Community
  • 1
  • 1
sifriday
  • 4,342
  • 1
  • 13
  • 24
  • Thanks @sifriday. I was afraid of that. I also assumed there is code somewhere in KO that is actually doing that already and I could just take advantage of it and not have to write a parser. – Grandizer Oct 15 '14 at 15:41
  • 1
    yep, I thought exactly the same thing, which is why I spent quite a while puzzling away at that other question I've linked to! I thought that if I could find that code then (1) it would be useful and (2) I'd learn lots about KO in the process. But I didn't find the code or even learn much new about KO, beyond concluding that it's quite smart and needs a lot more study to work out exactly what's going on. But I bet there is an option somewhere to call some internal KO functions to do this. – sifriday Oct 15 '14 at 15:46