Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

How to run time add classes, attributes, and styles for cells? #34

Open
masaoliou opened this issue Feb 17, 2015 · 14 comments
Open

How to run time add classes, attributes, and styles for cells? #34

masaoliou opened this issue Feb 17, 2015 · 14 comments

Comments

@masaoliou
Copy link

I need to dynamically add classes, attributes, and styles for cells. According to examples row-template.html and dom-elements.html, it seems to me that sortable-table requires occurences of and <td></td> to be staticly coded like this:

<sortable-column name="fruit">Fruit</sortable-column>
<sortable-column name="alice">Alice</sortable-column>

<td style="width:10%;"><input type="number" value="{{record.row.alice}}" style="text-align:right;"></td>

My problem is that these occurences are unknown in design time. They are determined by input JSON data. Hence I have this clumsy design:

<sortable-table rowTemplate="rt">
    <template repeat="{{MyData.columns}}">
        <sortable-column class="{{color}}">{{title}}</sortable-column>
    </template>
    <template id="rt" bind="{{MyData.data as data}}">
        <template repeat="{{c,i in data}}">
            <td><input type="{{MyData.columns[i].type}}" required?="{{MyData.columns[i].required}}" value="{{record.row[i]}}" style="text-align:{{MyData.columns[i].type == 'number' ? 'right' : 'left'}};"></td>
        </template>
    </template>
</sortable-table>
<script>
document.querySelector("sortable-table").MyData={
    columns:[
        {title:"fruit",type:"text",color:"red",required:true}
        ,{title:"alice",type:"number",color:"blue",required:true}
        ,{title:"bill",type:"number",color:"yellow"}
        ,{title:"casey",type:"number",color:"green"}
    ]
    ,data:[
        ["apple",4,10,2],["banana",0,4,0],["grape",2,3,5],["pear",4,2,8],["strawberry",0,14,0]
    ]
};
</script>

Notice that I avoid adding column names in input JSON data in order to save some bandwidth.

My code has one big issue - the value entered by user in <input></input> loses 2-way data binding!

What is the correct design patten in my case?
Thank you!

@stevenrskelton
Copy link
Owner

I couldn't quite get your code to work as you explain it, but I'll explain how I would do it and hopefully it works for your use-case.

Assigning to sortable-table.MyData is a bit heavy-handed, it's probably better to stick to the component's exposed API.

<template id="example2" bind>
    <sortable-table rowTemplate="rt" data="{{myData}}" args="{{myArgs}}">
        <template repeat="{{myColumns}}">
            <sortable-column class="{{color}}">{{title}}</sortable-column>
        </template>
        <template id="rt">
            <template repeat="{{column,i in columns}}">
                <td><input type="{{args[i].type}}" required?="{{args[i].required}}" value="{{record.row[i]}}" style="text-align:{{args[i].type == 'number' ? 'right' : 'left'}};"></td>
            </template>
        </template>
    </sortable-table>
    <json-stringify data="{{myData}}" id="dump"></json-stringify><button onclick="document.getElementById('dump').dataChanged();">Refresh</button>
</template>
<script>
window.addEventListener('polymer-ready', function(){
    document.getElementById('example2').model = {
        myColumns:[
            {title:"fruit"},
            {title:"alice"},
            {title:"bill"},
            {title:"casey"}
        ],
        myArgs:[
            {type:"text",color:"red",required:true},
            {type:"number",color:"blue",required:true},
            {type:"number",color:"yellow"},
            {type:"number",color:"green"}
        ],
        myData:[
            ["apple",4,10,2],["banana",0,4,0],["grape",2,3,5],["pear",4,2,8],["strawberry",0,14,0]
        ]
    };
});
</script>

I was working on column level args, but never finished them. They would make things a lot simpler - instead of worrying about indexes you could reference them directly from the column; ie: column.args.required.

In my example I renamed myColumns (and created a second myArgs array) to make it clear that the columns in the rt template is being passed in by the sortable-table code, and comes from the parsed sortable-column nodes. But if you aren't confused with how template variables are scoped, reuse names:

<template id="example2" bind>
    <sortable-table rowTemplate="rt" data="{{data}}" args="{{columns}}">
        <template repeat="{{columns}}">
            <sortable-column class="{{color}}">{{title}}</sortable-column>
        </template>
        <template id="rt">
            <template repeat="{{column,i in columns}}">
                <td><input type="{{args[i].type}}" required?="{{args[i].required}}" value="{{record.row[i]}}" style="text-align:{{args[i].type == 'number' ? 'right' : 'left'}};"></td>
            </template>
        </template>
    </sortable-table>
    <json-stringify data="{{data}}" id="dump"></json-stringify><button onclick="document.getElementById('dump').dataChanged();">Refresh</button>
</template>

<script>
window.addEventListener('polymer-ready', function(){
    document.getElementById('example2').model = {
        columns:[
            {title:"fruit",type:"text",color:"red",required:true}
            ,{title:"alice",type:"number",color:"blue",required:true}
            ,{title:"bill",type:"number",color:"yellow"}
            ,{title:"casey",type:"number",color:"green"}
        ]
        ,data:[
            ["apple",4,10,2],["banana",0,4,0],["grape",2,3,5],["pear",4,2,8],["strawberry",0,14,0]
        ]
    };
});
</script>

I'm not sure what you mean about breaking 2-way binding... the underlying arrays are passed by reference so you are likely working with the same data but it's just not triggering a data refresh. By default only indexes are observed for changes (array[i]), and not particular elements within indexes (array[i].field won't trigger a change). This is a whole different can of worms 😈 and is how Polymer normally works.

@masaoliou
Copy link
Author

Thank you very much for taking time explaining! I learn much from your kind help.

@masaoliou
Copy link
Author

Class my-class does not appear in DOM with this code:

<template repeat="{{columns}}">
    <sortable-column class="my-class">{{title}}</sortable-column>
</template>

What is the correct way to inject classes into <th></th>?

@masaoliou masaoliou reopened this Feb 19, 2015
@masaoliou
Copy link
Author

Never mind!
Chromium's debugger shows the following correct DOM tree for the aforementioned code:

<sortable-column class="my-class">fruit</sortable-column>
<sortable-column class="my-class">alice</sortable-column>
<sortable-column class="my-class">bill</sortable-column>
<sortable-column class="my-class">casey</sortable-column>

while it displays empty child node under <template repeat="{{columns}}"> for my real use case like this:

<template repeat="{{columns}}">
  #document-fragment   
</template>

Perhaps I am misusing property columns (and naming?). I am going to do some research and try to figure out the whole concept.

@masonlouchart
Copy link

If the class to be defined is constant, you can set an ID on the sortable-table and apply your style using the CSS rules through the Shadow DOM.

@masaoliou
Copy link
Author

Ooops! I closed my question too soon! My last question remains effective - How to dynamically add classes to element <th></th>?

The following code

<template repeat="{{columns}}">
    <sortable-column class="{{class}}">{{title}}</sortable-column>
</template>

now creates child nodes within <template repeat="{{columns}}"> like this:

<template repeat="{{columns}}">
  <sortable-column class="red">fruit</sortable-column>
  <sortable-column class="blue">alice</sortable-column>
  <sortable-column class="yellow">bill</sortable-column>
  <sortable-column class="black">casey</sortable-column>
</template>

However, this correct result is not quite useful. What I need is to
add classes to element <th></th> instead, like this:

<thead>
  <tr>
    <th class="red">fruit</th>
    <th class="blue">alice</th>
    <th class="yellow">bill</th>
    <th class="blck">casey</th>
  </tr>
</thead>

@LM450N: The classes to be added are not constants. Thank you!

@masaoliou masaoliou reopened this Feb 19, 2015
@masonlouchart
Copy link

I did not tried this but I think, if the sortable-column component does not allow use a template to do that, you will have to extend the sortable-column and add a thClass attribute for example.

@stevenrskelton
Copy link
Owner

I left out support for setting class and thClass attributes in sortable-column because I was hoping there would be a better solution using dynamic CSS or templates.

Web Components "best practices" are non-existent, I probably should add support for it because it looks like it could help some of you out.

@masaoliou
Copy link
Author

Please close this issue for me after implementing this feature to which I am looking forward.

@masaoliou
Copy link
Author

I seem to have no ending questions :-(

Running the above two examples you kindly provided, I change numeric value in <input> element. The JSON data type in attribute <sortable-table> data bound to <input type="number"> is automatically altered to "text" data type, as shown by <json-stringify> dumps:

Original data:

[
    ["apple",4,10,2],
    ["banana",0,4,0],
    ["grape",2,3,5],
    ["pear",4,2,8],
    ["strawberry",0,14,0]
]

After changing value in <input> (notice the double quotes surrounding 5 in the first row):

[
    ["apple","5",10,2],
    ["banana",0,4,0],
    ["grape",2,3,5],
    ["pear",4,2,8],
    ["strawberry",0,14,0]
]

The data updated by users will be sent back to server and thus its JSON data types must be predictable. Can sortable-table survive user's data modification and preserve the original JSON data types in its data attribute?

@masaoliou
Copy link
Author

Adding attribute datatype to sortable-column in example1 seems to be not helping much:

myColumns:[
    {type:"text",title:"fruit"},
    {type:"number",title:"alice"},
    {type:"number",title:"bill"},
    {type:"number",title:"casey"}
]
<template repeat="{{myColumns}}">
    <sortable-column datatype="{{type}}">{{title}}</sortable-column>
</template>

@stevenrskelton
Copy link
Owner

Standard <input> will always return a String value (even with type=number), so standard Polymer data-binding will always save the field as a String.

I'm not sure what the best approach for this would be, it likely would be to inherit a new control from the standard input that behaves nicely.

@masaoliou
Copy link
Author

Then maybe I will manipulate the data types of the result model using Number() or the like before sending it to server. :-)

@masaoliou
Copy link
Author

Behaviors of checkbox and multiSelection become unstable if rows and columns are dynamically generated. After adding properties checkbox="true" multiSelect="false" to element <sortable-table> in the two examples you provided,

  • Check boxes does not respond to the first mouse click. They start to respond only after the second and more mouse clicks.
  • Single row selection is not enforced. Multiple rows are always allowed to be selected even though property multiSelect is explicitly set to false.

Enlightenment will be greatly appreciated!

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

No branches or pull requests

3 participants