Media List with swipeout and check box to select item

Currently I have in my App A media List with swipeout action, and when the client click the item it opens the page details of that Item, I need to implement an action that the client can select multiple items and execute an action in all these selected ones (like delete selected by a button in the bottom of the page), to implement this, I created 2 Lists, 1 list with the swipeout action and details link, and another list (hidden) without the swipe and without details link but with a check box in the beginning of the item, so when the client want to delete multiple, I have the main media list and I show the send list with check box.
I did this solution because I didn’t managed to add a check box to the media Item with link, and when I implement it, once the client try to click the check box, it open the details page.

Do any one has solution for my case, in order to keep only 1 list with swipe action and details link together with check box

before you change it to your code
go to => https://framework7.io/kitchen-sink/core/
open console
and copy/paste this code
to see how it’s working

app.views.current.routes.unshift({
  path: '/some-page/',
  component: {
    data: function() {
      return {
        edit: false,
        vlData: { items: [] }
      };
    },
    methods: {
      mark: function(){ this.$app.dialog.alert('Mark'); },
      toggle: function(){ this.$setState({ edit: !this.edit }); }
    },
    on: {
      pageInit: function(){
        var self = this;
        self.virtualList = self.$app.virtualList.create({
          el: self.$el.find('.virtual-list'),
          height: self.$theme.ios ? 63 : (self.$theme.md ? 73 : 46),
          items: [...Array(10000).keys()].map(function(i){ i++;
            return { id: i, title: 'Item '+i, subtitle: 'Subtitle '+i };
          }),
          renderExternal: function(vl,vlData) {
            self.$setState({ vlData: vlData });
          }
        });
      }
    },
    template: `
    <div class="page">
      <div class="navbar">
        <div class="navbar-bg"></div>
        <div class="navbar-inner sliding">
          <div class="left"></div>
          <div class="title">Virtual List VDOM</div>
          <div class="right">
            <a href="#" class="link" @click="toggle">
              {{#if edit}}Done{{else}}Edit{{/if}}
            </a>
          </div>
        </div>
      </div>
      <div class="page-content">
        <div class="list virtual-list virtual-list-vdom media-list">
          <ul>
            {{#each vlData.items}}
            <li key="{{id}}" class="swipeout" 
                style="top: {{../../vlData.topPosition}}px">
              {{#unless @root.edit}}
              <div class="swipeout-content">
                <a href="#" class="item-link item-content">{{else}}
                <label class="item-checkbox item-content">
                  <input type="checkbox" name="name" value="{{id}}"/>
                  <i class="icon icon-checkbox"></i>{{/unless}}
                  <div class="item-inner">
                    <div class="item-title-row">
                      <div class="item-title">{{title}}</div>
                    </div>
                    <div class="item-subtitle">{{subtitle}}</div>
                  </div>{{#unless @root.edit}}
                </a>
              </div>
              <div class="swipeout-actions-left">
                <a href="#" class="color-orange" @click="mark">Mark</a>
              </div>{{else}}</label>{{/unless}}
            </li>
            {{/each}}
          </ul>
        </div>
      </div>
    </div>
    `
  }
});
app.views.current.router.navigate('/some-page/');
1 Like

Thanks plpl for the response

the example seems to be working, but I have a problem using the example provided,
currently i’m building my virtual list once the DOM is loaded by using the below code

var virtualList = app.virtualList.create({}); // complete code below

while the example you provided to me is using a template for a component

i’m not sure I will be able to adjust it to my code, my complete example page code as below, if you can help to adjust it that will be great

code base of this link: https://mbplautz.github.io/example/f7-virtuallist-stickyheader-demo.html

<html class="ios device-pixel-ratio-1 device-desktop device-windows">
<head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <title>My App</title>

    <link rel="stylesheet" href="css/framework7.bundle.min.css">
    <link rel="stylesheet" href="css/framework7-icons.css">

    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <style type="text/css">
        .moving-div {
            position: relative;
        }

        .list.virtual-list ul div.list-group.moving-div ul {
            padding-left: 0;
        }
    </style>

</head>
<body>
<div id="app">
    <div class="view view-main view-init">
        <div class="page">
            <div class="navbar">
                <div class="navbar-bg"></div>
                <div class="navbar-inner sliding">
                    <div class="title">Virtual List</div>
                    <div class="subnavbar">
                        <form data-search-container=".virtual-list" data-search-item="li" data-search-in=".item-title"
                              class="searchbar searchbar-init">
                            <div class="searchbar-inner">
                                <div class="searchbar-input-wrap">
                                    <input type="search" placeholder="Search"/>
                                    <i class="searchbar-icon"></i>
                                    <span class="input-clear-button"></span>
                                </div>
                                <span class="searchbar-disable-button if-not-aurora">Cancel</span>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
            <div class="searchbar-backdrop"></div>
            <div class="page-content">
                <div class="list simple-list searchbar-not-found">
                    <ul>
                        <li>Nothing found</li>
                    </ul>
                </div>
                <div class="list virtual-list media-list searchbar-found">

                </div>
            </div>
        </div>
    </div>
</div>

<script src="js/jquery-3.4.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="plugins/formhelpers/js/bootstrap-formhelpers.min.js"></script>
<script type="text/javascript" src="js/framework7.bundle.min.js"></script>
<script>var theme = 'ios';
if (location.href.indexOf('theme=md') >= 0) theme = 'md';
if (location.href.indexOf('theme=aurora') >= 0) theme = 'aurora';
var plugin = {
    params: {
        theme: theme,
        root: '#app',
    }
};
if (Framework7.use) Framework7.use(plugin);
else if (Framework7.Class && Framework7.Class.use) Framework7.Class.use(plugin);

    var app = new Framework7();

    // Dummy items array
    var items = [];
    var j = 1;
    for (var i = 1; i <= 1000; i++) {
        items.push({
            title: 'Item ' + i,
            subtitle: 'Subtitle ' + i,
            index: i,
            header: 'header ' + j
        });

        if (i % 100 === 0) {
            j++;
        }
    }

    function createItemDiv() {
        var d = document.createElement('div');
        d.className = 'list-group moving-div';
        var u = document.createElement('ul');
        d.appendChild(u);
        return {d, u};
    }

    function onVirtualBeforeInsert(vlist, fragment) {
        var self = this;
        var list = Dom7(vlist.ul);

        var virtualItems = [];
        var first = true;
        var top;
        while (fragment.hasChildNodes()) {
            var c = fragment.removeChild(fragment.firstChild);
            if (first) {
                first = false;
                top = c.style.top;
            }
            Dom7(c).css('top', '');
            virtualItems.push(c);
        }

        var previousName = null;
        var addedHeaders = [];
        var e;

        virtualItems.forEach(item => {
            var headerName = Dom7(item).attr('data-header-name');
            if (previousName !== headerName) {
                previousName = headerName;
                e = createItemDiv();
                fragment.appendChild(e.d);
                Dom7(e.d).css('top', top);
                var h = document.createElement('li');
                h.className = 'list-group-title';
                h.appendChild(document.createTextNode(headerName));
                e.u.appendChild(h);
                addedHeaders.push(e);
            }

            e.u.appendChild(item);

        });

        vlist.setListSize();
        list.css({height: `${list.height() + ((app.theme == 'ios' ? 80 : 48) * addedHeaders.length)}px`});
    }

    var virtualList = app.virtualList.create({
        // List Element
        el: '.virtual-list',
        // Pass array with items
        items: items,
        // Custom search function for searchbar
        searchAll: function (query, items) {
            var found = [];
            for (var i = 0; i < items.length; i++) {
                if (items[i].title.toLowerCase().indexOf(query.toLowerCase()) >= 0 || query.trim() === '') found.push(i);
            }
            return found; //return array with mathced indexes
        },
        // List item Template7 template
        itemTemplate:
            '<li data-header-name="{{header}}">' +
            '<a href="#" class="item-link item-content">' +
            '<div class="item-inner">' +
            '<div class="item-title-row">' +
            '<div class="item-title">{{title}}</div>' +
            '</div>' +
            '<div class="item-subtitle">{{subtitle}}</div>' +
            '</div>' +
            '</a>' +
            '</li>',
        // Item height
        height: app.theme === 'ios' ? 80 : (app.theme === 'md' ? 73 : 46),
        on: {
            itemsBeforeInsert: onVirtualBeforeInsert
        }
    });


    // create searchbar
    var searchbar = app.searchbar.create({
        el: '.searchbar',
        searchContainer: '.virtual-list',
        searchIn: '.item-subtitle, .item-title',
        on: {
            search(sb, query, previousQuery) {
                console.log(query, previousQuery);
            }
        }
    });
</script>
</body>
</html>

i lost you at this line:

<script src="js/jquery-3.4.1.min.js"></script>

there is no way i’m going to touch it (we’re in 2020)
i’m sorry

it will be hard to do it differntly (possible, but very hard)

take a look here => https://framework7.io/docs/router-component.html#main-app-component
you can use my code like this:

var app = new Framework7({
  component: {
    data: function() { },
    methods: function() { },
    on: { }
  }
})

Line was included for some testing only, so you can just remove it

<script src="js/jquery-3.4.1.min.js"></script>  

so actually it will not be used, also all the jquery API’s are exist in one way or another in F7

I tried to adjust the code you provided to my project, but without any luck

I managed to trigger the edit button action and changed the edit from false to true, but after that I was not able to rebuild/refresh/re-render the virtual list

it’s so much appropriated your help, as I’m still in the beginning of using this framework

my modified source as below

<html class="ios device-pixel-ratio-1 device-desktop device-windows">
<head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <title>My App</title>

    <link rel="stylesheet" href="css/framework7.bundle.min.css">
    <link rel="stylesheet" href="css/framework7-icons.css">

    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <style type="text/css">
        .moving-div {
            position: relative;
        }

        .list.virtual-list ul div.list-group.moving-div ul {
            padding-left: 0;
        }
    </style>

</head>
<body>
<div id="app">
    <div class="view view-main view-init">
        <div class="page">
            <div class="navbar">
                <div class="navbar-bg"></div>
                <div class="navbar-inner sliding">
                    <div class="title">Virtual List</div>
                    <div class="right">
                        <a href="#" class="link" @click="toggle">
                            Edit
                        </a>
                    </div>
                    <div class="subnavbar">
                        <form data-search-container=".virtual-list" data-search-item="li" data-search-in=".item-title"
                              class="searchbar searchbar-init">
                            <div class="searchbar-inner">
                                <div class="searchbar-input-wrap">
                                    <input type="search" placeholder="Search"/>
                                    <i class="searchbar-icon"></i>
                                    <span class="input-clear-button"></span>
                                </div>
                                <span class="searchbar-disable-button if-not-aurora">Cancel</span>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
            <div class="searchbar-backdrop"></div>
            <div class="page-content">
                <div class="list simple-list searchbar-not-found">
                    <ul>
                        <li>Nothing found</li>
                    </ul>
                </div>
                <div class="list virtual-list media-list searchbar-found">

                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="js/framework7.bundle.min.js"></script>
<script>var theme = 'ios';
if (location.href.indexOf('theme=md') >= 0) theme = 'md';
if (location.href.indexOf('theme=aurora') >= 0) theme = 'aurora';
var plugin = {
    params: {
        theme: theme,
        root: '#app',
    }
};
if (Framework7.use) Framework7.use(plugin);
else if (Framework7.Class && Framework7.Class.use) Framework7.Class.use(plugin);

    var app = new Framework7({
        iosTranslucentBars: false,
        iosTranslucentModals: false,
        panel: {
            swipe: 'left',
        },
        data: function () {
            return {
                selDataIdx: null,
                edit: true
            };
        },
        methods: {
            mark: function(){ app.dialog.alert('Mark'); },
            toggle: function(){ app.data.edit = !app.data.edit; }
        }
    });

    // Dummy items array
    var items = [];
    var j = 1;
    for (var i = 1; i <= 1000; i++) {
        items.push({
            title: 'Item ' + i,
            subtitle: 'Subtitle ' + i,
            index: i,
            header: 'header ' + j
        });

        if (i % 100 === 0) {
            j++;
        }
    }

    function createItemDiv() {
        var d = document.createElement('div');
        d.className = 'list-group moving-div';
        var u = document.createElement('ul');
        d.appendChild(u);
        return {d, u};
    }

    function onVirtualBeforeInsert(vlist, fragment) {
        var self = this;
        var list = Dom7(vlist.ul);

        var virtualItems = [];
        var first = true;
        var top;
        while (fragment.hasChildNodes()) {
            var c = fragment.removeChild(fragment.firstChild);
            if (first) {
                first = false;
                top = c.style.top;
            }
            Dom7(c).css('top', '');
            virtualItems.push(c);
        }

        var previousName = null;
        var addedHeaders = [];
        var e;

        virtualItems.forEach(item => {
            var headerName = Dom7(item).attr('data-header-name');
            if (previousName !== headerName) {
                previousName = headerName;
                e = createItemDiv();
                fragment.appendChild(e.d);
                Dom7(e.d).css('top', top);
                var h = document.createElement('li');
                h.className = 'list-group-title';
                h.appendChild(document.createTextNode(headerName));
                e.u.appendChild(h);
                addedHeaders.push(e);
            }

            e.u.appendChild(item);

        });

        vlist.setListSize();
        list.css({height: `${list.height() + ((app.theme == 'ios' ? 80 : 48) * addedHeaders.length)}px`});
    }

    var virtualList = app.virtualList.create({
        // List Element
        el: '.virtual-list',
        // Pass array with items
        items: items,
        // Custom search function for searchbar
        searchAll: function (query, items) {
            var found = [];
            for (var i = 0; i < items.length; i++) {
                if (items[i].title.toLowerCase().indexOf(query.toLowerCase()) >= 0 || query.trim() === '') found.push(i);
            }
            return found; //return array with mathced indexes
        },
        // List item Template7 template
        itemTemplate:`<li key="{{index}}" class="swipeout" data-header-name="{{header}}">
            {{#unless @root.edit}}
              <div class="swipeout-content">
                <a href="#" class="item-link item-content">
                {{else}}
                <label class="item-checkbox item-content">
                  <input type="checkbox" name="name" value="{{index}}"/>
                  <i class="icon icon-checkbox"></i>
                  {{/unless}}
                  <div class="item-inner">
                    <div class="item-title-row">
                      <div class="item-title">{{title}}</div>
                    </div>
                    <div class="item-subtitle">{{subtitle}}</div>
                  </div>
                  {{#unless @root.edit}}
                </a>
              </div>
              <div class="swipeout-actions-left">
                <a href="#" class="color-orange" @click="mark">Mark</a>
              </div>
              {{else}}
              </label>
              {{/unless}}
            </li>
        `,
        // Item height
        height: app.theme === 'ios' ? 80 : (app.theme === 'md' ? 73 : 46),
        on: {
            itemsBeforeInsert: onVirtualBeforeInsert
        }
    });


    // create searchbar
    var searchbar = app.searchbar.create({
        el: '.searchbar',
        searchContainer: '.virtual-list',
        searchIn: '.item-subtitle, .item-title',
        on: {
            search(sb, query, previousQuery) {
                console.log(query, previousQuery);
            }
        }
    });
</script>
</body>
</html>

ok

this is a working example

before you make any changes, just use it as it is
after that you can can do it in your way

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My App</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta http-equiv="Content-Security-Policy" content="default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content:">
    <link rel="stylesheet" href="css/framework7.bundle.min.css">
  </head>
  <body>
    <div id="app"></div>
    <script src="js/framework7.bundle.min.js"></script>
    <script>
    var app = new Framework7({
      root: '#app',
      theme: 'auto',
      component: {
        data: function() {
          return { edit: false, vlData: { items: [] } };
        },
        methods: {
          mark: function(){ this.$app.dialog.alert('Mark'); },
          toggle: function(){ this.$setState({ edit: !this.edit }); }
        },
        on: {
          pageInit: function(){
            var self = this;
            self.virtualList = self.$app.virtualList.create({
              el: self.$el.find('.virtual-list'),
              height: self.$theme.ios ? 63 : (self.$theme.md ? 73 : 46),
              items: [...Array(10000).keys()].map(function(i){ i++;
                return { id: i, title: 'Item '+i, subtitle: 'Subtitle '+i };
              }),
              renderExternal: function(vl,vlData) {
                self.$setState({ vlData: vlData });
              }
            });
          }
        },
        template:`
        <div id="app">
          <div class="view view-main view-init">
            <div class="page">
              <div class="navbar">
                <div class="navbar-bg"></div>
                <div class="navbar-inner">
                  <div class="left"></div>
                  <div class="title">Virtual List VDOM</div>
                  <div class="right">
                    <a href="#" class="link" @click="toggle">
                      <span>{{#if edit}}Done{{else}}Edit{{/if}}</span>
                    </a>
                  </div>
                </div>
              </div>
              <div class="page-content">
                <div class="list virtual-list virtual-list-vdom media-list">
                  <ul>
                    {{#each vlData.items}}
                    <li key="{{id}}" class="swipeout" 
                        style="top: {{../../vlData.topPosition}}px">
                      {{#unless @root.edit}}
                      <div class="swipeout-content">
                        <a href="#" class="item-link item-content">{{else}}
                        <label class="item-checkbox item-content">
                          <input type="checkbox" name="name" value="{{id}}"/>
                          <i class="icon icon-checkbox"></i>{{/unless}}
                          <div class="item-inner">
                            <div class="item-title-row">
                              <div class="item-title">{{title}}</div>
                            </div>
                            <div class="item-subtitle">{{subtitle}}</div>
                          </div>{{#unless @root.edit}}
                        </a>
                      </div>
                      <div class="swipeout-actions-left">
                        <a href="#" class="color-orange" @click="mark">Mark</a>
                      </div>{{else}}</label>{{/unless}}
                    </li>
                    {{/each}}
                  </ul>
                </div>
              </div>
            </div>
          </div>
        </div>
        `
      }
    });
    </script>
  </body>
</html>
1 Like

Works as expected

many thanks