Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boolean special attribue #3

Closed
uiii opened this issue Jun 17, 2013 · 32 comments
Closed

Boolean special attribue #3

uiii opened this issue Jun 17, 2013 · 32 comments

Comments

@uiii
Copy link
Contributor

uiii commented Jun 17, 2013

Hi, why attributes like data-show/hide have their values evaluated with isAttributeDefined function? I think there is more suitable conversion to boolean value.

Consider I have variable (non-boolean but can be null) to render in a div element and I want to show that div only if the variable is non null. With current implementation a have to create another control variable or a helper (to boolean coversion):

scope.variable = ...;
scope.showVariable = scope.variable != null;
<div data-show="{{scope.showVariable}}">{{scope.variable}}</div>

If the attribute values will be converted to boolean I can do this:

scop.variable = ...;
<div data-show="{{scope.variable}}">{{scope.variable}}</div>

This is more confortable and clear I think.

@soundstep
Copy link
Owner

The reason is that you can use the attribute (data-show and data-hide) without value:

<div data-hide></div>

But the normal use is with a Boolean:

scope.showVariable = true;
scope.showVariable = false;
<div data-show="{{showVariable}}"></div>

You can find an example there:
http://soundstep.github.io/soma-template/#data-show-and-data-hide

Hope that helps!

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

Ok, so why don't just make special case when the attribute is empty and in other cases convert to boolean?

@soundstep
Copy link
Owner

I'm not sure what you mean by that. All the following examples are valid:

<div data-show></div>
<div data-show=""></div>
<div data-show="{{showVariable}}"></div>

Also some IE versions need a value to the attribute, even empty.

Do you have an actual issue with the way it is implemented? Can you provide me a plunkr or jsfiddle with what you would like to do?

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

Only problem is a necessity of declaring one more variable which inform whether to show element or not. As I mentioned in the first post it can be done using only one variable.

@soundstep
Copy link
Owner

I see, I'll check that out to see if that implementation would cause any problems.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

Ok thanks. If you want I can help you with that, but I'm not so much familiar with the source code yet.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

Maybe it is enough to modify isAttributeDefined function in 3.helpers.js file

function isAttributeDefined(value) {
    return (value === '' || (Boolean(value) && value != 'false') || !isDefined(value));
}

@soundstep
Copy link
Owner

You can do a pull request if you want, you'll just need Grunt to compile and the tests are in /tests for both dev and minified.

As long as the tests for true, false and no value go through, it should be ok:
https://github.com/soundstep/soma-template/blob/master/tests/spec/api/special-attributes.js#L65

isAttributeDefined is used for different attributes, might need to double check all of these.

Otherwise I'll have a look when I got time.

@soundstep
Copy link
Owner

Another reason I did that if I remember well is that the attribute without value comes to be "undefined" in IE 7, or the other way around.
That would mean a difference between undefined and null, which might not be obvious to everyone.

This would show:

scope.showVariable = undefined;

And this would hide:

scope.showVariable = null;

Not that friendly.
Need to verify and think about it.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

Hmm, it looks complicated. The best solution will be to define the default value to the attributes. So when the attribute without value is specified its value is set to default one. But this needs more intervention to the code.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

And there comes another question, why is there the option to use the attribute without value? Is there someone using it? It is actually just a shorthand for style="display: none/block". The necessity to explicitly specify the value won't be so painful, will be?

@soundstep
Copy link
Owner

Using an attribute without value is part of the html5 spec, angular is following the same spec I noticed.

http://developers.whatwg.org/common-microsyntaxes.html#boolean-attribute
http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#boolean-attributes
http://developers.whatwg.org/elements.html#custom-data-attribute

I don't follow exactly the spec as I should remove the attribute completely if it is false. I planned to do it with all non-used attribute like that, never had the time.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

But the spec speaks about boolean attributes where the state is indicateded according to their presence. They cannot have 'true' or 'false' values only the value same at their name or empty string. So for the data-show should be possible only data-show, data-show=data-show or data-show="".

@soundstep
Copy link
Owner

Yes, or data-show sharp.
Anyway being user-friendly is very important, I'll think about all this and see what I can do.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

I just wanted to say that data-show isn't boolean attribute according to the html5 spec, so the option to not specify the value is needless even makes thing more complicated.

@uiii
Copy link
Contributor Author

uiii commented Jun 17, 2013

BTW: Angular is doing it right, according to me, ng-show accepts truthy values (everything what is true after coversion to boolean) but does not accept empty values. You can try it here http://plnkr.co/edit/Zn7QzzYsRKuaPKunUIqK?p=preview

@soundstep
Copy link
Owner

ok interesting, I got some of it wrong.
With angular, I thought having the attribute without value was actually showing, which is not the case.
An empty string is false in javascript, so the angular way makes sense (they probably just interpret the value).

So I might just implement it the same way.

@soundstep
Copy link
Owner

Working on it

@uiii
Copy link
Contributor Author

uiii commented Jun 18, 2013

I like to hear that, but wait a minute. Don't you think about implementing support for javascript-like expressions in tokens (e.g. {{ var1 + fun1(fun2(var2), var3) }})? I think it's related to this issue. I'm already facing another issue with that. I wanted to show an element if two conditions are satisfied: data-show="first($index) && var". Then I realized that this is not supported. I remmebered there are helper, so I thought I got it I will make and(...) helper. So I did that: data-show="and(first($index), var)". Unfortanately this nested function call isn't supported too.

I've looked into angular's documentation how are they deal with that and they has their own expression parser instead of using eval() function. I don't know details why is it so.

@soundstep
Copy link
Owner

Angular is using the new constructor on Function to evaluate their content.
But I won't build a real parser in soma-template, this is out of scope. It would probably dramatically increase the size of the lib, which is already big...

You can evaluate thing yourself by calling a function though. Here are some examples.

Without parameter:

<div data-show="{{isValid()}}"/>
scope.var1 = 'something';
scope.var2 = {};
scope.isValid = function() {
    return scope.var1 && scope.var2;
}

With parameter:

<div data-show="{{isValid(var1, var2, 'string')}}"/>
scope.var1 = 'something';
scope.var2 = {};
scope.isValid = function(v1, v2, str) {
    return scope.var1 && scope.var2 && str;
}

@uiii
Copy link
Contributor Author

uiii commented Jun 18, 2013

Yes I know I can compute it in the code, but my problem is that I need to evaluate two conditions which are otherwise completely unrelated. I'm rendering document splited into pages and I want to show some information on the first page only if the information is defined. Making helper isPageFirstAndInfoDefined(pageIndex, info) is conceptually bad.

The solution I wanted from you is to make a full expression parser or at least support function (helper) call as a parameter of another function's call, e.g. helper1(helper2(var1), var).

@soundstep
Copy link
Owner

If both your functions are on the scope, you can send only the parameters and call helper2 inside your helper1.

Note that you can also add functions helpers on the parent scope, the parsing of the expression is recursive through the scopes.

A bit more details, check the tests to see what's currently possible with the expressions:

https://github.com/soundstep/soma-template/blob/master/tests/spec/api/expression.js
https://github.com/soundstep/soma-template/blob/master/tests/spec/internal/expression.js

I won't build a parser because it is simply a lot slower, I started the lib like angular using "new Function", to quickly find out how slow it is (benchmarking it against handlebars and other template libraries). Instead I parse the expression without evaluating, much quicker but that means you can't write javascript in the html as you do with angular.

@uiii
Copy link
Contributor Author

uiii commented Jun 18, 2013

Ok, it is not ideal but I solve this using function defined on scope

scope.showInfo(pageIndex) {
    return pageIndex == 0 && scope.info != null;
}

But if you implement support for that pattern with function calls as parameters as I mentioned, you don't need to use eval() function or some own implementation. It will be enough to allow params to be interpolations too and when you will be evaluating root interpolation you will evaluate recursively the params first.

@soundstep
Copy link
Owner

I already parse parameters but not functions somehow, can't remember why.
I'll have a look into that.

Do you mind creating a new issue to parse functions in parameters?

Cheers.
On 18 Jun 2013 19:34, "Richard Jedlička" [email protected] wrote:

Ok, it is not ideal but I solve this using function defined on scope

scope.showInfo(pageIndex) {
return pageIndex == 0 && scope.info != null;}

But if you implement support for that pattern with function calls as
parameters as I mentioned, you don't need to use eval() function or some
own implementation. It will be enough to allow params to be interpolations
too and when you will be evaluating root interpolation you will
evaluate recursively the params first.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3#issuecomment-19631204
.

@uiii
Copy link
Contributor Author

uiii commented Jun 18, 2013

Ok, I'll create the new issue.

EDIT: #5

@uiii
Copy link
Contributor Author

uiii commented Jun 19, 2013

Great, you fix it :-) I'm not so sure what was your intentions but I made a test how your implementation works and how I suppose it to be working (not sure in every example): http://plnkr.co/edit/dhgZAZ2t8tupYkoOdOC0?p=preview

... just for your information

@soundstep
Copy link
Owner

Great thanks but don't forget there's no parser in the lib, this 2 examples are different, the first one is a string and the second is an object:

<span data-hide="{prop: 1}">
<span data-hide="{{thing}}">
scope.thing = {prop: 1};

@soundstep
Copy link
Owner

if you want more details, I basically check for:

true, "true", 1, "1" ---> returns true
false, "false", 0, "0" ---> returns false

if none of them, I let javascript decide:
!!value

This is needed because everything coming from tokens are string, in javascript !!"false" returns true, as false is a string and not a boolean.

Only the scope examples are valid. And:
!!{} --> is true
!![] --> is also true

It is very easy to test, you start node or open a console and add a double exclamation mark before anything you want to test.

@uiii
Copy link
Contributor Author

uiii commented Jun 19, 2013

Ok, that makes sense. Only the [] as a scope variable, will you fix that to be considered as true or will leave that as it is? As I checked angular, they consider it as false, but empty object as true: http://plnkr.co/edit/Zn7QzzYsRKuaPKunUIqK?p=preview

@uiii
Copy link
Contributor Author

uiii commented Jun 19, 2013

You have inconsistency in tests, you say in comment "value is true" but expect to be false:

it("data-hide with value", function () {
    // value is true
    ct.innerHTML = '<div data-hide="{{myValue}}"></span>';
    tpl.compile();
    tpl.scope.myValue = [];
    tpl.render();
    if (ie === 7) expect(ct.firstChild.getAttribute('data-hide')).toBeFalsy();
    else expect(ct.firstChild.getAttribute('data-hide')).toEqual('false');
    expect(ct.firstChild.style.display).toEqual('');
});

@uiii
Copy link
Contributor Author

uiii commented Jun 19, 2013

And check changelog, you have typo in your example.

@soundstep
Copy link
Owner

There's actually a problem with the empty array. Tokens are evaluated because you can have data-show="{{part1}}{{part2}}"

!!("" + {}) ---> returns true
!!("" + []) ---> returns false
!!("" + ["string"]) ---> returns true

I love javascript.

I'm not sure I'll change that one, javascript is javascript... I'll correct the test though.

(Thanks for the typo).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants