Where should I initialize select when using Materialize and Meteor? - javascript

I'm trying to use Materialize Forms on Meteor. On its Materialize's page it says I should init the "select" input field like this:
$(document).ready(function() {
$('select').material_select();
});
I've tried calling this on Meteor.startup, Template.body.created - nothing worked. I get the following error:
undefined is not a function (evaluating '$('select').material_select()')
Where should I initialize it?

Use the template's .rendered callback
<template name="hello">
<select><option>...</option></select>
</template>
Then you can have this in your js file
Template.hello.onRendered(function() {
$('select').material_select();
});
The template is added to the body most likely after rendered has already fired so thats why the body rendered didn't work. If you use .created the DOM hasn't rendered yet.

Akshat's answer is correct but if it still doesn't work for you, there might be a problem with subscription not fired yet or not every needed part of DOM beeing rendered. Use afterflush then.
Template.listing.onRendered(function () {
var template = this;
template.subscribe('listOfThings', function () {
Tracker.afterFlush(function() {
template.$('select').material_select();
});
});
});
Here is a conversation about that: https://github.com/meteor/meteor/issues/4401#issuecomment-103340262
And the docs: http://docs.meteor.com/api/tracker.html#Tracker-flush

Related

Getting Html From calling jQuery custom function

I created a custom function and am trying to call it in a separate function and get the elements html. What am I missing?
Custom Function: (wizard.js)
function wizard() {
var wizardBody = $(this).html();
console.log(wizardBody);
}
jQuery.fn.wizard = wizard;
Calling Said function on element: (config.js)
// Initiate Web Design Get Started Wizard
$("#webDesignGetStartedWizard").wizard();
I simply get undefined with the current code above, if I don't call the function on the html ($(this).html) then I get the following:
ƒ (e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w…
UPDATE
So the problem seems to be related to the use of my router plugin, jq-router Because if I take #webDesignGetStartedWizard out of the template and place it in my index file, it works perfectly.
https://github.com/muzammilkm/jq-router
It does work correctly make sure config.js and wizard.js loaded in the document correctly. You should load the wizard.js and then include config.js and also make sure the div#webDesignGetStartedWizard available when the script is called.
See a demo of your code.
function wizard() {
var wizardBody = $(this).html();
console.log(wizardBody);
}
jQuery.fn.wizard = wizard;
$(document).ready(function() {
// Initiate Web Design Get Started Wizard
$("#webDesignGetStartedWizard").wizard();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="webDesignGetStartedWizard">
<p>current text in the paragraph.</p>
</div>

Can not access jquery in function. "Variable Scope" issue?

My ultimate goal is to have dynamic autocompletion on text input using typeahead jquery library.
In my HTML I put the input text field like this:
<input class="typeahead form-control" name="author_1" id="author_1" type="text"
placeholder="Type a part of author name or surname">
then in javascript I have this:
<script type="text/javascript">
function typeahead_initialize() {
var path = "{{ route('instructor_name') }}";
$('.typeahead').typeahead({
source: function (query, process) {
return $.get(path, { query: query }, function (data) {
return process(data);
});
}
});
}
typeahead_initialize ();
// Here typeahead is recognized as a function
// $('.typeahead').typeahead('destroy');
// Begin jQuery
$(document).ready(function() {
$("#addAuthorBtn").click(function() {
addAuthor();
});
$("#removeAuthorBtn").click(function() {
removeAuthor();
});
function addAuthor () {
// Destroy the typeahead system
// The problem is here: typeahead is not recognized
// as a functon here. It seems I can not reach
// jquery functions here.
$('.typeahead').typeahead('destroy');
// I add the dynamic input here, I deleted the unrelated code
// Reinitialize typeahead system again to include
// the dynamically added text input
typeahead_initialize ();
}
function removeAuthor () {
// Destroy the typeahead system
$('.typeahead').typeahead('destroy');
// I remove the dynamic input here, I deleted the unrelated code
// Reinitialize typeahead system again to include
// the dynamically added text input
typeahead_initialize ();
}
});
</script>
I explained in comment where the problem is. The auto-completion works for the first static input. But the second dynamically added input is not offering autocomplete, which I suspect I must destroy the typeahead and rerun it again. I guess the 'this' object inside addAuthor() function does not point to the same 'this' which is outside of $(document).ready(function() {}). How can I solve this? I cannot destroy and reinitialize the typeahead system when user clicks the add or remove button.
The official error I get is:
TypeError: $(...).typeahead is not a function
Update:
This is not related to forgetting to include jquery library, these are the libraries that I have included so far (I am using Laravel so the formatting is a bit odd) I guess it is related to "scope". The autocompletion actually works! so it means "typeahead" library is included properly. It just does not work when I call the typeahead from "inside a function".
<script src="{{ URL::to('js/jquery-3.1.1.js') }}"></script>
<script src="{{ URL::to('js/bootstrap3-typeahead.min.js') }}"></script>
Thing you did wrong is invoking typeahead_initialize() in script directly. Put this function in document.ready and all will go smoothly.
I just copied your code and did some modification check jsfiddle below, see log in console also
https://jsfiddle.net/Ld1wcy03/
OK I found the problem by myself. The problem comes because I use Laravel and it adds some scripts via app.js at the button of my view that causes this problem.
Probably the Laravel framework adds jQuery library by itself and two instances of it causes this issue.Removing the Laravel scripts solved the issue.

ReactJS componentDidMount does not behave as expected with Bootstrap

I want to call $(dom).popover() on a rendered DOM. So I have:
module.exports = React.createClass({
componentDidMount: function() {
$(this.getDOMNode()).popover();
},
render: function() {
return ( // My DOM );
}
})
This returns error: TypeError: $(...).popover is not a function. BUT if I put a delay in the componentDidMount, then it works, i.e.:
componentDidMount: function() {
var _this = this;
setTimeout(function () {
$(_this.getDOMNode()).popover();
}, 250);
}
How can I accomplish the same thing without using setTimeout?
Try placing your jquery code inside a $(document).ready().
E.G. :
componentDidMount: function() {
var _this = this;
$(document).ready(function() {
$(_this.getDOMNode()).popover();
});
}
Edit #1: In response to comment: "You should also explain why" – Rohit Gupta
If you ask why too much, it will destroy the wonder of it all.
I joke. I came across the answer because I was having the same problem as the OP. I was using jQuery to reinitialize a Materialize.css accordion widget(which uses jQuery) in my componentDidMount function--or at least I was trying. But it wasn't working like I figured it should.
Then I came here, saw the OP had tried using setTImeout, and it worked; I tried it; it worked for me--even at 1ms--then I had the idea that slapping in a document(ready) function might work since basically it does something similar to the componentDidMount lifecycle function. $(document).ready listens for the entire document to load before it runs anything in the callback--compondentDidMount listens for the component to mount before it runs anything.
When you put a $(document).ready function inside the componentDidMount function(and put all the stuff into the former that which would typically only be in the latter), it will delay the code in the componentDidMount function until the whole document is loaded rather than just the component that the componentDidMount function resides in. The popover function acts on some element of the page that has yet to load. With the OP's original code you can manually call the popover event in the console after the page has loaded, which will then initialize the effect, which implies that the element that is needed for the popover does not exist when componentDidMount is called, but does exist after the page is fully loaded--which is why $(document).ready works: that is when its callback is triggered.
That is my theory atleast :) Any alternative theories of why it works?
if you defined a import literal in your code, please try removing.
// import * as $ from "jquery";
Make sure you have correctly included your js files like this:
<html>
<body>
...
<script src="jquery.js"></script>
<script src="bootstrap.js"></script>
<!-- Your js file that starts React app -->
<script src="myapp.js"></script>
</body>
</html>

Jquery not firing click() to fire with a div id

In my erb file, I have a script like this:
function checkJquery() {
if(window.jQuery) {
jQuery(document).ready(function() {
alert('onready');
$('div#habla_topbar_div').click(function() {
alert('onclick');
});
});
}
else {
window.setTimeout(checkJquery, 1000);
}
}
I get the 'onready' alert, but the 'onclick' alert does not work. Any idea what I might doing wrong?
Edit:
The div is part of the Olark chat integration and the erb file has nothing except the configuration for that and the above script.
The div 'habla_topbar_div' is defined.
Image:
Several Rails-specific issues here
--
Delegation
Because most Rails applications use Turbolinks or similar, you have to delegate your Javascript (typically) from the document object:
#app/assets/javascripts/application.js
$(document).on("click", "#habla_topbar_div", function() {
alert('onclick');
});
If you use this without embedding with other JS functions, it should work, considering you have a div with id=habla_topbar_div.
--
Turbolinks Events
Secondly, you want to ensure you replace the standard $(document).ready function with one of the Turbolinks event hooks. You'll want to do the following:
#app/assets/javascripts/application.js
var your_function = function(){
...
}
$(document).on("page:load ready", your_function);
Your code looks good, maybe you don't have a div with id="habla_topbar_div".
Check this Fiddle to find out where did you wrong.
Update:
If you already have a div with id="habla_topbar_div" maybe the Id's value has been changed by some code or application before rendering or on runtime.
Try to add an unique class name to "habla_topbar_div" div like this:
<div id="habla_topbar_div" class="habla_topbar_div_unique habla_topbar_div_normal ...">
and use this:
$('.habla_topbar_div_unique').click
instead of this:
$('div#habla_topbar_div').click
Check Fiddle Demo

Combination of $(document).ready and $scope.$on('$viewContentLoaded', function() {...})

I'm using JQuery UI components in a view of AngularJS/JQuery application.
I need something like this (does not work) in my JavaScript code:
$(document).ready(function () {
var elem = $('div[ng-view]')[0];
var $scope = angular.element(elem).scope();
$scope.$on('$viewContentLoaded', function() {
// multiple JQuery statements
// to support JQuery-UI componenets
});
});
This code is included as <script> into index.html that has <div class="container" ng-view> element.
My thinking was that we need a two-step process:
First JQuery reacts on document-ready HTML event and attaches a listener to Angular's $viewContenLoaded using $scope retrieved using [ng-view] element.
Then each time a view is loaded my JQuery code will be executed and JQuery UI components get activated and wired.
Apparently my logic is flawed somewhere. Please point me in the right direction.
ADDITIONAL INFO (posted 03/31/14):
The rest of my code (controllers, service, routing) is written in TypeScript.
That element needs to be compiled in order to bind angulars scope to that element. You could try something like:
var scope = angular.injector(['ng']).get('$rootScope').$new();
var compile = angular.injector(['ng']).get('$compile');
compile(elem)(scope);
scope.$on('$viewContentLoaded', function(){
// Your code
});
Though I would suggest putting your code in a directive. The code I shown above is nothing more than a hack and dangerous since now you have global access to your services.

Categories