Calling a function on a table of async populted data in Meteor - datatable

I am using Meteor and the jquery data table library.
I have a table that I am loading meteor/handlebars using something like this:
<table>
{{each x}}
// code to insert rows of data
{{/each}}
</table>
I need to call this on the table once it has been fully populated with data to turn it into a sortable table:
$('#tableID').dataTable();
It works when I call it from the console once the DOM is fully loaded and the data is in there, but using Template.rendered() doesn't work, nor does listening for changes with .observe since the data is loaded before that particular view is rendered.
Where can I run this code to ensure that the data is fully loaded, and if there are updates to the data in the table that it will run again?

I found a way of doing it which seems to work after I split the individual rows into templates - will update as I continue to debug it (and this is certainly not ideal).
Template.individualRow.rendered = function() {
if (!$('#tableID').hasClass('initialized')) {
$('#tableID').not('.initialized').addClass('initialized').dataTable();
};
};

It beats me, but I can suggest a way to work around:
Do not use handlbars' helper, and observe the data, when the data changed, re-render the template manually, like this:
<table id="tableID"></table>
<template name="yourTemplate">
{{#each this}}
<tr>....</tr>
{{/each}}
</template>
var query = ...
query.observe({
added: function () {
manuallyRender();
},
changed: function () {
manuallyRender();
},
...
});
function manuallyRender() {
// don't need Meteor.render here, because you don't need it to be reactive.
var frag = $(Template.yourTemplate(query.fetch()));
$('#tableID').empty().append(frag).dataTable();
}
It should work, but maybe not the best way, any thoughts?
Actually, I think meteor has a long way to go...

Related

How to use interval in alpine js?

I am trying to add a timer to my cards. So, what I do is. I receive data from DB and pass the data to the records[] array and then I show the data on frontend.
<template x-for="record in records">
<span x-text="record.created_at">2022-10-31 18:41:20</span>
</template>
But what I want is to show the seconds between created at to now time and the seconds should be changed as time passes kind of like how much time is passed since the record is created.
Actually, in my case, I need to change the text within the loop. I tried something like this
https://cdn.jsdelivr.net/gh/kevinbatdorf/alpine-magic-helpers#latest/dist/interval.js
<template x-for="record in records">
<span x-text="$interval(getTimer(record), 1000)">2022-10-31 18:41:20</span>
</template>
<script type="text/javascript">
function dateComponent() {
return {
init: function() {
// fetch data
},
getTimer: function(record) {
console.log(record)
}
}
}
</script>
But this runs the interval only once. Is this possible in alpine js. and if yes, please guide me to where I am doing wrong.

Binding Vue.js to all instances of an element, without(?) using Components

Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.

How to use ng-repeat with an ajax request with DataTables?

Let's say I'd like to load a dataTable dynamically, but instead of using normal ajax function within datatable, I'd like to load it through Angular, use ng-repeat to generate the tr elements and then apply DataTables. Would that be possible? In broad terms, how could I do that?
I've got the ajax/ng-repeat working already like this:
angular.module('myApp', ['ngResource', 'ui.bootstrap'])
.factory('ProjectsService', ['$resource', function($resource) {
return $resource('/customers');
}])
.controller('AdminCustomersCtrl', ['ProjectsService', '$scope', function(ProjectsService, $scope) {
$scope.projects = ProjectsService.query();
}]);
The reason I'd like to do this is that all elements (data in the table) would be binded, so whenever the user edit an entry in the table, the change would be acknowledged immediately (visually) and also saved to the REST server. Each entry in the table would correspond to a mongo db document. So if anyone has any ideas of how to achieve this differently, I'd love suggestions.

Form select box in Backbone Marionette

I'm trying using Backbone.Marionette to build an application. The application gets its data through REST calls.
In this application I created a model which contains the following fields:
id
name
language
type
I also created an ItemView that contains a complete form for the model. The template I'm using is this:
<form>
<input id="model-id" class="uneditable-input" name="id" type="text" value="{{id}}"/>
<input id="model-name" class="uneditable-input" name="name" type="text" value="{{name}}" />
<select id="model-language" name="language"></select>
<select id="model-type" name="type"></select>
<button class="btn btn-submit">Save</button>
</form>
(I'm using Twig.js for rendering the templates)
I am able to succesfully fetch a model's data and display the view.
What I want to do now is populate the select boxes for model-language and model-type with options. Language and type fields are to be restricted to values as a result from REST calls as well, i.e. I have a list of languages and a list of types provided to me through REST.
I'm contemplating on having two collections, one for language and one for type, create a view for each (i.e. viewLanguageSelectOptions and viewTypeSelectOptions), which renders the options in the form of the template I specified above. What I am not sure of is if this is possible, or where to do the populating of options and how to set the selected option based on data from the model. It's not clear to me, even by looking at examples and docs available, which Marionette view type this may best be realized with. Maybe I'm looking in the wrong direction.
In other words, I'm stuck right now and I'm wondering of any of you fellow Backbone Marionette users have suggestions or solutions. Hope you can help!
Create a view for a Select in my opinion is not needed in the scenario that you are describing, as Im assuming that your languages list will not be changing often, and the only porpouse is to provide a list from where to pick a value so you can populate your selects in the onRender or initializace function of your view using jquery.
you can make the calls to your REST service and get the lists before rendering your view and pass this list to the view as options and populate your selects on the onRender function
var MyItemView = Backbone.Marionette.ItemView.extend({
initialize : function (options) {
this.languages = options.languages;
this.typeList = options.typeList;
},
template : "#atemplate",
onRender : function () {
this.renderSelect(this.languages, "#languagesSelect", "valueofThelist");
this.renderSelect(this.typeList, "#typesSelect", "valueofThelist")
},
renderSelect :function (list, element, value) {
$.each(list, function(){
_this.$el.find(element).append("<option value='"+this[value]+"'>"+this[value]+"</option>");
});
}
})
var languagesList = getLanguages();
var typeList = getTypesList();
var myItemView = new MyItemView({languages:languagesList,typeList :typeList });
Hope this helps.

getting value of several textarea from WYMeditor

I would like getting the value of two textarea from WYMeditor:
The first one:
<script type="text/javascript">
jQuery(function() {
$(" .wymeditor").wymeditor({
logoHtml: '',
lang: 'fr',
skin: 'default',
});
});
</script>
And the second one:
<script type="text/javascript">
jQuery(function() {
$(" .wymeditor_ref").wymeditor({
logoHtml: '',
lang: 'fr',
skin: 'silver',
});
});
</script>
HTML:
<textarea id="definition" class="wymeditor" name="definition"/></textarea>
<textarea id="references_definitions" class="wymeditor_ref" name="definition"/></textarea>
I'm using this: WYMeditor.INSTANCES[0].html();
But, the problem is I don't know how to do if there are two textarea. How getting the second value?
Thanks a lot!!
Get specific WYMeditor instance HTML with known ordering
If you simply want to iterate through the results of all the WYMeditor instances on a particular page, your array index method is just fine. If you know the order in which the WYMeditor instances are created, you'll do something like:
var wymResults,
wymRefResults;
wymResults = WYMeditor.INSTANCES[0].xhtml();
wymRefResults = WYMeditor.INSTANCES[1].xhtml();
Get HTML from all WYMeditor instances
If you have an unknown number of instances of WYMeditor, this is how you might get the results of all of them:
var results = [],
i;
for (i = 0; i < WYMeditor.INSTANCES.length; i++) {
// Do something with the xhtml results
results.push(WYMeditorINSTANCES[i].xhtml());
}
Get specific HTML results with unknown instantiation order
If it matters which WYMeditor instance you'd like to retrieve though, which is often the case, you'll want to store references to the specific instances when you create them. eg.
var wym,
wymRef,
wymResults,
wymRefResults;
// Instantiate my WYMeditor instances
wym = $(".wymeditor").wymeditor();
wymRef = $(".wymeditor_ref").wymeditor();
// Let's grab the results. This will probably live in some kind of `submit()` handler.
wymResults = wym.xhtml();
wymRefResults = wymRef.xhtml();
Use xhtml(), not html()
Another note specific to your example, but you should be using the xhtml() call instead of the html() call to ensure consistent, cross-browser markup.
The html() call doesn't run the resulting HTML through the parser or do any browser-specific cleanup, which means that if you were to load some html in lets say IE9 that was created in Chrome, just calling html() without making any changes will mean the resulting HTML will be slightly different. Different browsers need HTML that is slightly different to provide a consistent editing experience, and WYMeditor abstracts this away for you, assuming you use xhtml() to get the results.

Resources