Why isn't meteor injecting the text from my template helpers? - javascript

Im trying to dynamically generate a table of two different sets of data. My database isnt empty and the returns have been verified as well. but when i check the rendered page the corresponding html isnt there as if nothing as returned.
template/html:
<template name="room">
<div class="container-fluid">
<h1> Sprint Retrospective</h1>
<hr>
<div class="input-group">
<input type="text" class="form-control thoughts" placeholder="Thoughts..." aria-describedby="basic-addon1">
<span class="input-group-addon">
<input id="wentWell" type="checkbox" aria-label="..."> Went Well
</span>
<span class="input-group-addon">
<input id="wentWrong" type="checkbox" aria-label="..."> Went Wrong
</span>
<span class="input-group-btn">
<button class="btn btn-default" type="button">Submit!</button>
</span>
</div>
<hr>
{{#if haveCards}}
<div class="container-fluid">
<div class="row">
<div class="col-xs-6 col-sm-6">
<div class="row">Went Well</div>
{{#each wentWell}}
{{>card}}
{{/each}}
</div>
<div class="col-xs-6 col-sm-6">
<div class="row">Went Wrong</div>
{{#each wentWrong}}
{{>card}}
{{/each}}
</div>
</div>
</div>
{{/if}}
</div>
</template>
Javascript:
"use strict";
/**
*
**/
var Cards = new Mongo.Collection('cards');
var allCards;
var wentWellCards;
var wentWrongCards;
if(Meteor.isClient){
Tracker.autorun(function(){
allCards = Cards.find({},{sort:{createdAt:-1}});
wentWellCards = Cards.find({category:"Went Well"},{sort:{createdAt:-1}});
wentWrongCards = Cards.find({category:"Went Wrong"},{sort:{createdAt:-1}});
});
Template.room.helpers({
haveCards: function(){
if(allCards != null && allCards != undefined && allCards.length > 0)
return true;
return false;
},
wentWell: function(){
return this.wentWellCards;
},
wentWrong: function(){
return this.wentWrongCards;
}
});
}

Jeremy answer its actually more in point, but..
Lets try to fix that code a little bit.
Lets change the wentWell and wentWrong helpers to look more clean like this.
wentWell: function(){
return Cards.find({category:"Went Well"},{sort:{createdAt:-1}});
},
wentWrong: function(){
return Cards.find({category:"Went Wrong"},{sort:{createdAt:-1}});
}
Also for the haveCards helpers you can do something like
haveCards: function(){
return Cards.find().count() >= 1 //for example or return just findOne()
}

Your helpers should return wentWellCards instead of this.wentWellCards, etc.

Your helpers are not reactive, so, when the data is loaded (which happens after the page is rendered) the helpers are not re-run.
Simply, call the reactive methods (the minimongo queries) in the helper directly. This will get them re-run once the data is available
Also, when you check the count, you need to fetch the collection
Cards = new Mongo.Collection('cards');
if(Meteor.isServer){
Meteor.publish('cards', function() {
return Cards.find({},{sort:{createdAt:-1}});
});
}
if(Meteor.isClient){
Template.room.onCreated(function(){
this.subscribe('cards');
});
Template.room.helpers({
haveCards: function(){
var allCards = Cards.find({},{sort:{createdAt:-1}}).fetch();
return (allCards != null && allCards != undefined && allCards.length > 0);
},
wentWell: function(){
return wentWellCards = Cards.find({category:"Went Well"},{sort:{createdAt:-1}});
},
wentWrong: function(){
return wentWrongCards = Cards.find({category:"Went Wrong"},{sort:{createdAt:-1}});
}
});
}
And you will need to publish the collection from the server and subscribe from the template (unless you are using autopublish)

Related

Reset response to null after search input is cleared Vue.js

How do I reset a my response to NULL, as it is in my data upon the clearing/deleting of the query in my search bar?
I've vaguely achieved this with v-show and a query length, but I know its not really correct because it's hiding the results, not actually clearing them from the DOM. I also tried tying an ELSE statement to the query method with no luck.
<div class="searchBarContainer">
<div class="search">
<div class="searchBar">
<form v-on:submit="queryGitHub(query)">
<input type="search" placeholder="Search Repositories Ex. Hello
World" v-model="query" />
<button type="submit" v-on:click="isHidden =
!isHidden">Search</button>
</form>
</div>
<div class="results" id="results" v-if="response" v-show="query.length =
0">
<div class="notFound" v-if="response.length == 0">
<p>Sorry buddy, try another search!</p>
</div>
<div class="resultsHeadings" v-if="response.length >= 1">
<p>Name</p>
<p>Language</p>
</div>
<div class="items" v-if="response.length >= 1">
<div class="item" v-for="(item, index) in response, filteredList"
v-bind:id="item.id" :key="index">
<p>{{item.name}}</p>
<p>{{item.language}}</p>
<div class="expand">
<a #click="pushItem(index)">
<div class="itemButton">
<button v-on:click="addFave(item.id, item.forks)">Add to
Favorites</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
export default{
data () {
return {
query:'',
response: null,
items: [],
faves: [],
activeItems: [],
}
},
methods: {
queryGitHub(q) {
if (q.length >= 1){
fetch('https://api.github.com/search/repositories?q=' + q)
.then((j) => {
return j.json();
})
.then ((r) => {
console.log(r);
//this.response = r.items;
this.response = r.items.slice(0, 15)
})
}
}
}
};
I need my search input to remove the response by resetting it to NULL once the input has been cleared by the visitor. Presently if you clear the input, the results disappear which is great but if you type again, the results just reappear. So they are hidden, not removed. I believe I need a function, possibly via computed, to set the response in data back to null upon the clearing of the input.
You could attach an input event handler to your input element and inside it you'll check the length of the query string. If it's zero, then set response to null.
<input type="search" placeholder="Search Repositories Ex. Hello
World" v-model="query" #input="onQueryChange" />
The onQueryChange function should be under methods instead of computed since it's not returning any derived data.
methods: {
onQueryChange(event) {
// can be this.query.length === 0 as well
if(event.target.value.trim().length === 0) {
this.response = null;
}
}
}

jQuery find() & each() on dynamic elements

I've got a <div> element that contains multiple other <div>'s that are populated dynamically using jQuery/Ajax, I'm trying to run the following code but find() fails to get any of them.
Here's my HTML boilerplate prior to my data being populated.
<input type="text" id="inv-filter" class="form-control">
<div class="row itemList" style="margin-right: -2px;margin-left:-2px;">
</div>
And here's what it looks like after population.
<input type="text" id="inv-filter" class="form-control">
<div class="row itemList" style="margin-right: -2px;margin-left:-2px;">
<div class="col-xs-3 col-sm-2 shop-item" data-hash="Item Name 1">
</div>
<div class="col-xs-3 col-sm-2 shop-item" data-hash="Item Name 2">
</div>
......
</div>
My Javascript looks like the following:
$('#inv-filter').keyup(function() {
var search = $(this).val().toLowerCase();
var $sellContainer = $('.itemList');
if (search.trim() === '') {
$sellContainer.find('.shop-item').show();
$sellContainer.find('.shop-item.selected').hide();
return;
}
$sellContainer.find('.sell-item').each(function() {
if (!$(this).hasClass('selected') && $(this).data('hash').text().toLowerCase().includes(search)) {
$(this).show();
} else {
$(this).hide();
}
});
});
I've ran multiple tests inside console debugger such as $('.itemList').length() etc.. but doesn't appear to find any results & when entering text into my input field, nothing is happening
$sellContainer.find('.sell-item').each(function() { //replace sell-item with shop-item
if (!$(this).hasClass('selected') && $(this).data('hash').text().toLowerCase().includes(search)) {
$(this).show();
} else {
$(this).hide();
}
});

Adding objects to the DOM after adding new data to the list

I have an array of objects which i populate on a button click.
When populating this array i make sure that i only add 10 objects to it.
When this is all loaded in the dom i give the user the oppertunity to add a few more objects.
I do this like this:
$scope.Information = [];
$.each(data, function (i, v) {
if (i<= 9)
$scope.Information.push(data[i]);
if(i >= 10) {
cookieList.push(data[i]);
}
}
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
$(".showMore").removeClass("hidden");
}
$(".showMore").on("click", function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
});
part of the HTML:
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
My issue: When i add objects to my array of objects "$scope.Information.push(obj);" I assumed that they would get added in the DOM but they do not, how do i do this the angular way?
EDIT MY SOLOUTION:
edited the HTML to use ng-click and the method is as follows:
$scope.addMore = function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
SetSpinner('show');
$.each(obj, function(i,v) {
$scope.Information.push(v);
});
SetSpinner('hide');
}
Here is the angular way:
 The view
<!-- Reference your `myapp` module -->
<body data-ng-app="myapp">
<!-- Reference `InfoController` to control this DOM element and its children -->
<section data-ng-controller="InfoController">
<!-- Use `ng-click` directive to call the `$scope.showMore` method binded from the controller -->
<!-- Use `ng-show` directive to show the button if `$scope.showMoreButton` is true, else hide it -->
<button data-ng-click="showMore()" data-ng-show="showMoreButton">
Show more
</button>
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
</section>
</body>
The module and controller
// defining angular application main module
var app = angular.module('myapp',[])
// defining a controller in this module
// injecting $scope service to the controller for data binding with the html view
// (in the DOM element surrounded by ng-controller directive)
app.controller('InfoController',function($scope){
$scope.Information = [];
$scope.showMoreButton = false;
// Bind controller method to the $scope instead of $(".showMore").on("click", function() {});
$scope.showMore = function(){
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
};
$.each(data, function (i, v) {
if (i<= 9) $scope.Information.push(data[i]);
if(i >= 10) cookieList.push(data[i]);
});
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
//$(".showMore").removeClass("hidden");
$scope.showMoreButton = true; // use $scope vars and ng-class directive instead of $(".xyz").blahBlah()
}
});
You should not use JQuery, use ng-click to detect the click, because angular has no idea when JQuery is done and when it needs to refresh the interface

ObservableArray not binding to GUI

I'm new with knockout.js and trying to fix data binding on a site that is build on Laravel and is using knockout.js.
Observable array works well and items can be pushed and popped without issues. The problem is with the binding to GUI. When items are pushed to array those are added to GUI, but nothing else works, like removing items, and also when adding more items later on those are added on the top of the GUI element list, not added after existing items on the GUI. The observable array is having correct items after push/pop/removeall, its just not reflecting to GUI.
I guess that the problem is that observable array is not binded to GUI, but I cannot figure out what could be wrong.
Stripped code:
Chat.init = function(){
Chat.viewModel = new Chat.ViewModel;
ko.applyBindings(Chat.viewModel, $('#msg_canvas').get(0));
};
Chat.ViewModel = function(){
self.messages = ko.observableArray();
self.setMessages = function(msgs){
_.each(msgs, function(msg){
self.messages.push(msg);
});
};
self.clearMessages = function(data, e){
self.messages.removeAll();
}
}
clearMessages is called via onclick: data-bind="click: $parent.clearMessages
The HTML is this:
<div id="msg_canvas" class="msg-wrap col-md-12"
style="height:274px;overflow-y:scroll;" data-bind="foreach: messages">
<div class="media msg">
<div class="media-body">
<span data-bind="text: sent_at"></span>
<small class="col-lg-10" data-bind="text: message"></small>
</div>
</div>
Any help or pointer to what could be causing the problem would be highly appreciated.
UPDATE: added inner HTML which was not included to post before
You need to have a control inside the div to hold your messages, like a <span> or <p>. Otherwise, you're simply doing the foreach without outputting the values. So your div should look something like this, using $data to access the value:
<div id="msg_canvas" data-bind="foreach: messages">
<p data-bind="text: $data"></p>
</div>
Here's a working snippet based on your code (setMessages slightly modified / hard coded with values):
ViewModel = function(){
self.messages = ko.observableArray([]);
self.setMessages = function(){
var msgs = ['message','message','message'];
_.each(msgs, function(msg){
self.messages.push(msg + ' ' + self.messages().length);
});
};
self.clearMessages = function(data, e){
self.messages.removeAll();
}
self.removeMessage = function(item){
self.messages.remove(item);
}
};
ko.applyBindings(new ViewModel());
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="msg_canvas" class="msg-wrap col-md-12"
style="height:274px;overflow-y:scroll;border: black solid 1px" data-bind="foreach: messages">
<p data-bind="text: $data"></p>
<input type="button" data-bind="click: removeMessage" value="Remove Item" />
</div>
<input type="button" data-bind="click: setMessages" value="Add Message" />
<input type="button" data-bind="click: clearMessages" value="Remove All" />

Jquery stop repeat my code with after?

I have a problem with my input and my Jquery :
Basically I have this code :
HTML:
<form id="formUser">
<div class="row">
<div class="small-8">
<div class="row">
<div class="small-6 columns">
<label for="right-label" class="right inline">First name</label>
</div>
<div class="small-6 columns">
<input type="text" name="fisrtName" placeholder="First name">
</div>
</div>
</div>
</div>
<div class="row text-center">
<input type="checkbox" class="check"><label for="checkbox"><p>I accept the review agreement</p></label>
<button type="submit" class="button join">Let's Go !</button>
</div>
</form>
JS :
<script type="text/javascript">
$(function(){
$("#formUser").submit(function(){
if(!$('input[name="fisrtName"]').val()) {
$('input[name="fisrtName"]').addClass("error");
$('input[name="fisrtName"]').after("<small class='error'>Invalid entry</small>");
}
return false;
});
});
</script>
And I have this
When I click several time on the button... the error class is repeat ..
How can i stop the repeat or incrase the actual class error ?
Maybe you should be doing something like this
$("#formUser").submit(function(){
var $element = $('internal[name="fisrtName]');
// ^ save this much faster ^
// ^ you have spelt firstName wrong also ^
// check val and check next element isn't error
if($element.val() && $element.next().hasClass('error') === false) {
$element.addClass('error').after("<small class='error'>Invalid entry</small>");
} else {
// now remove it if you need to
}
return false;
});
Hope it helps.
You should always cache your elements
By doing this:
$('internal[name="fisrtName');
$('internal[name="fisrtName');
$('internal[name="fisrtName');
You're calling the jQuery function 3 times when you do not need to.
You can use:
if($('input[name="fisrtName"]').find('.error').length==0)
$('input[name="fisrtName"]').after("<small class='error'>Invalid entry</small>");
Or:
var $firstName = $('input[name="fisrtName"]');
if (!$firstName.hasClass("error")) {
$firstName.addClass("error");
$firstName.after("<small class='error'/>");
}
$firstName.find("small.error").text("Invalid entry");
Try to change your code with this, I have aded the line "$('small.error').remove();"
$.q("#formUser").submit(function(){
if(!$.q('input[name="fisrtName"]').val()) {
$.q('small.error').remove();
$.q('input[name="fisrtName"]').addClass("error");
$.q('input[name="fisrtName"]').after("<small class='error'>Invalid entry</small>");
}
return false;
});

Categories

Resources