What are the rules for using jquery to manipulate the DOM?


#1

I’m having a really hard time with this one. Something that used to be very simple is taking me ages to figure out. I know it’s because I haven’t gotten over the hump with ember yet. Just getting a bit frustrated. I think there’s probably a more “ember” way to do this - but I have no idea what it is.

The issue: I have a long list of <li> 's. I only want to show the first 10, and have a “show more” link that toggles the rest of the list viewable (and changes text to “Show fewer”). I have this working just fine, but when the model refreshes, the “Show more” jquery stuff no longer works. I have no idea why. The click does nothing. No errors. It’s like the jquery vanishes.

My component code looks like this:

import Ember from 'ember';

let $ = Ember.$;

export default Ember.Component.extend({	

	didInsertElement() {
		this._super(...arguments);
		Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
	},
	
	afterRenderEvent() {	
	    $(".showToggle").click(function() {
	    	$('#' + $(this).data("bucket-id") + " .hidden").toggleClass("hide-hidden show-hidden");
	    	$(this).text($(this).text() === "Show More" ? "Show Fewer" : "Show More");
	    });   
	}
});

My component template looks like this. I am using the truth-helper to add css classes on the items that are to be toggled:

<style>
	.hide-hidden { display: none; }
	.show-hidden { display: block; }	
</style>

<ul id="{{filter_key}}">
{{#each filter_values as |value index|}}
	<li class="{{if (gte index 10) 'hidden hide-hidden'}}"><a href="#" {{action 'whatever'}}>{{value.key}} ({{value.count}})</a></li>
{{/each}}
</ul>
<a href="#" class="showToggle" data-bucket-id="{{filter_key}}" >Show More</a>

This renders out to:

<ul id="filter_size_jewelry">
	<li><a href="#" data-ember-action="" data-ember-action-493="493">4 (309)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-495="495">4.5 (306)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-497="497">5 (321)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-499="499">5.5 (313)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-501="501">6 (324)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-503="503">6.5 (314)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-505="505">7 (324)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-507="507">7.5 (313)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-509="509">8 (330)</a></li>
	<li><a href="#" data-ember-action="" data-ember-action-511="511">8.5 (304)</a></li>
	<li class="hidden hide-hidden"><a href="#" data-ember-action="" data-ember-action-513="513">9 (457)</a></li>
	<li class="hidden hide-hidden"><a href="#" data-ember-action="" data-ember-action-515="515">9.5 (248)</a></li>
	<li class="hidden hide-hidden"><a href="#" data-ember-action="" data-ember-action-517="517">10 (257)</a></li>
</ul>
<a href="#" data-bucket-id="filter_size_jewelry" class="showToggle">Show More</a>

#2

This can be done without jQuery.

Change <a> to fire action into the component. Have a separate computed property on the component that slice the first 10 of filter_values depending on another flag property on the component. Render this new property as part of {{each}} in the component’s template.


#3

Thanks. I did make some spaghetti that does something like that, and it works. Probably part of the learning process. I think the big issue is for me to approach problems in a different way: from the object-model perspective as opposed from the traditional DOM-oriented way.

Though I will say, I am not a huge fan of the rigid either/or ember attitude (not specific to ember, I realize). Learning ember is hard enough. Having to throw out all jquery almost makes it a dealbreaker. Yes, I know you can still use jquery for DOM stuff, but implementing it is a nightmare (at least for a beginner) and it’s obviously frowned upon.

jquery and jquery plugins have added an incredible amount of richness to the web, and are deeply ingrained. All at a relatively low cost to developers - low learning curve, doesn’t take alot of time/manpower to implement, alot of plugins available for re-use.

Ember and similar frameworks can provide the same level of richness and more, but the barriers to entry are enormous. It too me a whole day to figure out how to toggle a sub-set of a list without jquery (granted, I’m fairly dense), trying to re-create something like fancybox or bxslider in ember seems insurmountable.

Ember devs have gone to great lengths to make stuff “just work” - which is awesome. It’s amazing what you can do with just 2 lines of code. If that same approach was taken to allow those new to ember to EASILY employ some of their old, trusted methods (like jquery DOM stuff), it would help ease the transition.

But, I’m still probably looking at it wrong!


#4

I dealt with the same sort of hesitation/frustration when first diving into Ember but now feel quite comfortable. Granted it takes time. I remember my biggest thing was going from DOM eventing for communication between things to Ember actions, it’s a different concept all together.

Long story short, if you want to use jQuery plugins, make an Ember Component and use the didInsertElement() hook to “init” the jQuery plugin. Just remember to use the willDestroyElement() hook to destroy the plugin or you’ll get memory leaks. < This is due to the lifecycle of any front-end framework.

There is also the matter of getting the jQuery plugin itself included in the ember build. That deals with ember-cli and importing assets. I’d suggest first searching for an existing addon that’ll take care of all this for you. Or else you’ll want to read-up on managing dependencies and importing assets.

Also, I think this resource is still a valid way to implement jQuery plugins in ember: https://www.sitepoint.com/how-to-integrate-jquery-plugins-into-an-ember-application/


#5

Thanks, man. Good to know it gets easier :slight_smile: I just have to spend the time.


#6

The reason jQuery plugins are so easy to grab and toss in traditional server-side built web pages, is because you have the entire DOM available from the start. With any JS framework you don’t know what is going to populate the view/outlet, so that’s where I’ve had to rethink things too. When you use a component, you can use it’s life-cycle and it’s template and data like a mini website, just like @Panman8201 suggests. didnInsertElement() and other hooks can be your onReady type of thing. What type of things have you been trying to implement so far? Things like Masonry? I’d say that image-loading is still something that needs some love in ember-land. What I’m confused about is how to deal with jQuery things and fastboot!


#7

I was just frustrated because I couldn’t get my simple jquery to work, and I thought “this should be easier”. I think it’s that alot of ember newbs run into and it’s probably due to just thinking in a DOM-oriented fashion. As I learn more about ember, things are becoming easier.


#8

I guess my main point is that - it isn’t an ‘Ember’ issue. I’m working on an Angular 1.5 project right now, and it’s the same type of thing (but much less enjoyable)


#9

I wonder if later versions of angular have better compatibility with Jquery.


#10

It’s like you trying to make a picture out of coloured blocks and every time you turn around to get more blocks, someone muddles them up because their trying to make a different picutre using the same blocks. jQuery works outside of any JavaScript framework to access the DOM. This means that with any framework which keeps a representation of the DOM and does bookkeeping for speed and convenience reasons (Angular, Ember, React, Vue, … [all of them]) external code needs to fire at the right time and modify the DOM in a contained scope as to not break that representation. Therefore, one of these frameworks having generic compatibility with jQuery and all it’s plugins I don’t think is possible without a inordinate amount of effort

But this is missing the bigger picture of why these frameworks exist in the first place and I think it’s important to keep in mind. Frameworks take care of all the common problems we face when building JavaScript applications, which is usually focused on DOM access. It’s an abstraction that allows us to focus on the higher level of what we’re trying to achieve. jQuery does have a lot of useful plugins for doing all number of things, but as modern frameworks mature they too will build up a collection of useful plugins. As @Panman8201 said, it’s an investment, but much like learning to touch type it gets you up on another plane where you can be more efficient. A lot of what jQuery plugins do can be build in Ember (or any framework) because of the facilities they provide. It then allows that functionality to be used in FastBoot and other framework advances in the future with little to no change. Try to build in Ember without using jQuery and see how far you get. Were always here to help :slight_smile: