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

Scoped Mixins #1288

Open
metalshark opened this issue Nov 11, 2013 · 4 comments
Open

Scoped Mixins #1288

metalshark opened this issue Nov 11, 2013 · 4 comments

Comments

@metalshark
Copy link

It would be useful if mixins could be scoped/nested so that a parent mixin can determine child behaviour. For instance

mixin list
    mixin item
        li(attributes=attributes)
            block
    ul(attributes=attributes)
        block

mixin carousel
    mixin item
        .item(attributes=attributes)
            block
    .carousel(attributes=attributes)
        .carousel-inner
            block

+list
    +item foo
    +item bar

+carousel
    +item foo
    +item bar

whereby list and carousel treat the use of the item mixin differently due to the declaration being scoped inside the parent.

@ForbesLindesay
Copy link
Member

You could actually write mixins that behaved exactly like this as it stands, a little bit tricky I admit:

- var listType

mixin list
    - listType = 'list'
    ul(attributes=attributes)
        block

mixin carousel
    - listType = 'carousel'
    .carousel(attributes=attributes)
        .carousel-inner
            block
mixin item
    case listType
        when 'list'
            li(attributes=attributes)
                block
        when 'carousel'
            .item(attributes=attributes)
                block

@metalshark
Copy link
Author

Thanks @ForbesLindesay - that at least gives me a temporary solution/plan b.

Do not mind having to create a PR for this if the consensus is that nested mixins should be allowed - what are the jade dev's feelings about allowing this?

@ForbesLindesay
Copy link
Member

I'm not sure, there's a more general problem here. As it stands, blocks run in the scope in which they are defined, rather than the scope in which they are used. To demonstrate:

mixin foo()
    - var x = 10
    block
- var x = 20
+foo()
    = x

In the above example, we get 20 not 10. Some people have expressed a desire to have the ability to expose variables into the block. e.g. @jinhuatang in #1012:

mixin list(items) //- a list shell
    ul
        each item in items
            li
                block(link=link)

mixin link_list(links)
    +list(links)
        a(href=link.href)= link.text
mixin image_list(links)
    +list(links)
        img(src=link.src)
        = link.text

The goal here is very similar. What we're after is an explicit way to expose certain things to the body of a mixin. If we can find a nice way to do this, I'm all for it. At the moment I haven't come up with a nice way to express this intent in the code, or a nice way to implement it in the compiler.

@metalshark
Copy link
Author

Can see the problem and thanks for the detailed response.

Compiler aspects aside (haven't popped the hood of Jade yet) I have gotten used to passing attributes and attributes=attributes, so could a similar system be used for scope? Using the example from issue #1012 it is possible to use a global variable to make up for a lack of scope:

- var _scopedCurrentItem = null

mixin list()
    if attributes.items
        ul
            each item in attributes.items
                - _scopedCurrentItem = item
                li
                    block(attributes=attributes)
                - _scopedCurrentItem = null

mixin link_list()
    +list(attributes=attributes)
        a(href=_scopedCurrentItem.href)= _scopedCurrentItem.text

mixin image_list()
    +list(attributes=attributes)
        img(src=_scopedCurrentItem.src)
        = _scopedCurrentItem.text

- var myLinks = [ {href: 'https://www.google.co.uk', src: 'https://www.google.co.uk/images/srpr/logo11w.png', text: 'Google'}, {href: 'https://github.com', src: 'https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png', text: 'GitHub'}]

+image_list()(items=myLinks)

+link_list()(items=myLinks)

If I could just pass the scope object (holding all variables available at the time) the syntax would look like:

mixin list()
    if attributes.items
        ul
            each item in attributes.items
                li
                    //- Specify scope here so that block gets access to the item variable
                    block(attributes=attributes, scope=scope)

mixin link_list()
    //- Specify scope here so that any local variables are passed to list (e.g. items if not using attributes)
    +list(attributes=attributes, scope=scope)
        a(href=item.href)= item.text

mixin image_list()
    //- Specify scope here so that any local variables are passed to list (e.g. items if not using attributes)
    +list(attributes=attributes, scope=scope)
        img(src=item.src)
        = item.text

- var myLinks = [ {href: 'https://www.google.co.uk', src: 'https://www.google.co.uk/images/srpr/logo11w.png', text: 'Google'}, {href: 'https://github.com', src: 'https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png', text: 'GitHub'}]

+image_list()(items=myLinks)

+link_list()(items=myLinks)

or if you prefer a more explicit version

mixin list(items)
    if items
        ul
            each item in items
                - scope.item = item
                li
                    //- Specify scope here so that block gets access to the item variable
                    block(scope=scope)

mixin link_list(items)
    +list(items)
        a(href=scope.item.href)= scope.item.text

mixin image_list(items)
    +list(items)
        img(src=scope.item.src)
        = scope.item.text

- var myLinks = [ {href: 'https://www.google.co.uk', src: 'https://www.google.co.uk/images/srpr/logo11w.png', text: 'Google'}, {href: 'https://github.com', src: 'https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png', text: 'GitHub'}]

+image_list()(items=myLinks)

+link_list()(items=myLinks)

For my use case - all mixins defined within a mixin should be available when you are in the parent mixin. At the minute nested mixins are not available to call following a parent mixin's declaration. There would be no need to pass a scope variable, like with the first example.

@ForbesLindesay ForbesLindesay added this to the 3.0.0 milestone Oct 5, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants