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

Glimmer removes ability to access childViews from a component #11244

Closed
runspired opened this issue May 21, 2015 · 20 comments
Closed

Glimmer removes ability to access childViews from a component #11244

runspired opened this issue May 21, 2015 · 20 comments

Comments

@runspired
Copy link
Contributor

_childViews no longer exists and childViews is never populated. This makes it very difficult to build components that need ordered access to their child views.

@rwjblue
Copy link
Member

rwjblue commented May 21, 2015

childViews is populated in some cases, but not others. Ember itself does not use childViews to determine the children any longer, which is why childViews is getting out of sync (or not maintained at all) and we didn't notice.

Would be great to have a JSBin demo (I am disputing your statement though).

@mixonic
Copy link
Sponsor Member

mixonic commented May 21, 2015

@runspired a jsbin would help us know if this is directly related to #11170 or not.

@runspired
Copy link
Contributor Author

@mixonic it's not a ContainerView, so not sure it's related to that. It actually was a containerView until yesterday, but I reworked it (on 1.12) to be components and then moved to try out Glimmer and found this issue.

I'll whip up a JSBin.

@runspired
Copy link
Contributor Author

And yes, I know this was a private API, but it was a private API I've seen many have need for. I don't condone registering children this way for composable components, but in this case the children are children generated by the parent component.

@rwjblue
Copy link
Member

rwjblue commented May 21, 2015

A few alternatives for your scenario:

I think I like the latter better (it would be possible to use this in 1.12 with this.sendAction instead of this.attrs.register).

@runspired
Copy link
Contributor Author

@rwjblue the alternative I've implemented is to pass collection=this into the child, and have the child call collection.register(this) during int. register adds the child and orders it according to the child's content's index in the content array.

@runspired
Copy link
Contributor Author

The key and difficult thing is maintaining an accurate order, so I need to mirror changes to the array to changes in the generated childViews array.

@rwjblue
Copy link
Member

rwjblue commented May 21, 2015

In 1.13+ you could reset the array in the parents willRender and re-register it in the childs willRender, which will always guarantee the order. Sample: http://emberjs.jsbin.com/fufalu/3/edit

@runspired
Copy link
Contributor Author

I take it willRender is called even when the array only had a partial change?

@runspired
Copy link
Contributor Author

Answered my own question, it should: #10501

I think this is closable as the willRender / actions approach is workable, flexible and not too difficult to do.

@rwjblue
Copy link
Member

rwjblue commented May 21, 2015

workable, flexible and not too difficult to do

I also think it is easier to understand even for folks new to Ember (since it is much more declarative about what is going on) and has a dramatic lack of magical private APIs. 😺

@rwjblue rwjblue closed this as completed May 21, 2015
@topaxi
Copy link
Contributor

topaxi commented May 28, 2015

This also breaks http://stackoverflow.com/a/14328909/4737452 how can we implement this now in a easy way? I'm not sure how I can register the link-to helper / view in my component..

@timohofmeijer
Copy link

Having the same problem as @topaxi here. +1

@topaxi
Copy link
Contributor

topaxi commented Jun 3, 2015

@timohofmeijer I solved it by using the following, imho weird, solution:

components/active-link.js

const { computed } = Ember

Ember.LinkView.reopen({
  activeLink: computed({
    set(key, value) {
      value.set('linkView', this)
    }
  })
})

export default Ember.Component.extend({
  tagName:           'li',
  classNames:        [ 'active-link' ],
  classNameBindings: [ 'active' ],
  active:            computed.alias('linkView.active'),
  linkView:          null
})

templates/components/active-link.hbs:

{{yield this}}

Now I can use it like this:

{{#active-link as |link|}}
  {{#link-to 'login' activeLink=link}}Login{{/link-to}}
{{/active-link}}

I'm not sure if this is an acceptable solution though...

@timohofmeijer
Copy link

Looks pretty good to me @topaxi. Thanks for sharing. The thing with me is, I need the wrapper to get an active state when any of the nested link-to helpers becomes active (as in: more than 1). I moved back to a computed property observing currentPath, quite painless since my template in question was already being controlled by the application-controller.

@Kilowhisky
Copy link

@timohofmeijer Can you share your solution?

I was using childViews to mark menu items active on a navigation dropdown (e.g bootstrap navbar). With childViews not able to be observed tree type navigation structures are a bit difficult to work with now.

@timohofmeijer
Copy link

Basically a wrapper component with an active classBinding which observes route changes and then looks for .active classes. Not super elegant, but as long as you’re in your application scope –which has direct access to currentPath– it’s painless (else you’d need to pass along something that triggers on route transitions).

// active-link/component.js

export default Ember.Component.extend({
  classNameBindings: ['active'],

  // External Binding
  currentPath: null,

  // currentPath is just a trigger here.
  // You could also use didTransition from within a the route
  // combined with `notifyPropertyChange`
  active: Ember.computed('currentPath', ()=> {
    return this.$() && this.$('.active').length;
  })
});
// application/template.hbs

{{#active-link currentPath=currentPath}}
  {{#link-to "parent"}}Parent Route{{/if}}
  {{#link-to "parent.foo" class="is-hidden"}}{{/if}}
  {{#link-to "parent.bar" class="is-hidden"}}{{/if}}
{{/active-link}}

It’s a port from Alex Speller’s pre ember-1.13 solution:
https://github.com/alexspeller/ember-cli-active-link-wrapper

Within this repo’s issues, perhaps a more versatile solution is offered by @rwjblue:
alexspeller/ember-cli-active-link-wrapper#4

good luck @Kilowhisky

@Kilowhisky
Copy link

I ended up with a modified version of what @topaxi did. I turned linkView into an array and made it recursive. Still seems like a lot of boilerplate..

export default Ember.Component.extend({
    tagName: 'li',
    classNameBindings: ['active'],
    active: function(){
        return this.get('navLinks').any(function(item){
            return item.get('active');
        });
    }.property('navLinks.@each.active'),
    navLinks: null,
    initLinks: function(){
        this.set('navLinks', []);
    }.on('init'),
    activeNav: Ember.computed({
        set: function(key, value){
            value.get('navLinks').addObject(this);
        }
    }),
});
{{#nav-item class='dropdown' as |menu|}}
       {{#nav-item  activeNav=menu as |link|}}{{#link-to '' activeNav=link}}{{/link-to}}{{/nav-item}}
 {{/nav-item}}

@sukima
Copy link
Contributor

sukima commented Apr 19, 2016

These seem like very complex and convoluted solutions to a problem that should be real simple to manage. Why is the framework fighting something like this:

<nav role="navigation>
  <ul class="site-nav">
    {{#link-to "index" tagName="li" as |link|}}
      <button class="btn btn-default {{if link.active 'active'}}">Index</button>
    {{/link-to}}
    {{#link-to "route1" tagName="li" as |link|}}
      <button class="btn btn-default {{if link.active 'active'}}">Route 1</button>
    {{/link-to}}
    {{#link-to "about" tagName="li" as |link|}}
      <button class="btn btn-default {{if link.active 'active'}}">about</button>
    {{/link-to}}
  </ul>
</nav>

You could attempt this:

{{#link-to "about" tagName="li"}}
  <button class="btn btn-default {{if childViews.firstObject.active 'active'}}">about</button>
{{/link-to}}

But that seems weird and not supported. Is it really the intent to make link-to a mystery left to the unsure, non-experienced, new-comer, ember developer?

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

7 participants