Refresh collection on the browser - javascript

I have a templates:
<div id="search">
{{> search}}
</div>
<div id="bookmarks-container">
{{> bookmarks}}
</div>
<template name="bookmarks">
{{#each bookmarks}}
<div class="bookmark">
{{URL}}
</div>
{{/each}}
</template>
<template name="search">
<input type="text" id="search" value="" />
</template>
And I have a code to get bookmarks:
bookmarks = new Meteor.Collection("Bookmarks");
if (Meteor.isClient) {
Template.bookmarks.bookmarks = function() {
var s = $('#search').text();
alert(s);
if (s === '')
return bookmarks.find({});
else
return bookmarks.find({tags : 'some_tag'});
};
I want to display all bookmarks if #search text field is empty and filter my bookmarks if it is not.
As I see Template.bookmarks.bookmarks calls only once - when page loaded in the browser. How can I call this method every time when user press the button in the #search text input:
Template.search.events({
'keyup #search' : function(e,t) {
// ....
}
Thanks in advance.

Relay the data with Session
var s = Session.get("query");
and
Template.search.events({
'keyup #search' : function(e,t) {
Session.set("query",t.find('#search').value);
}
On its own the template bookmarks helper wouldn't know when to refresh the data. Using Session tells it that the query variable/hash is reactive.

Related

Updating unrelated Vue.js variable causing input values in template to disappear

I am having a weird issue with my single file Vue component where when I update an unrelated variable (Vue.js variable), all of my inputs (stuff I typed in, not the elements themselves.) disappear.
I have worked with Vue single file components for a few months now and I have never ran into something like this. Here is the weird part, the variable gets updated successfully as expected, but if I include the variable inside of the template at all that is when all the inputs disappear.
The function is looking up 'agents', then letting the user know how many records have been found and whether or not he/she would like to view them. If the user clicks on the "View" link, then they are shown a bootstrap-modal which shows them the records so that they could select one.
Here is what I have already tried:
Removing all ids from the inputs and using only refs="" to get the values.
changing the 'agents' variable name. Thought maybe it was conflicting with some rogue global or something.
Double checked that the parent component and this component was not being re-rendered. I did that by putting console.log() comments in the mounted() function and as expected it is only rendering once.
Watched the key using Vue dev tools extension to make sure the key was not being changed somehow.
Executed the searchAgent() function in a setTimeout(()=>{},5000) to see whether my use of _.debounce was causing issues.
Used jquery to fetch the values from the inputs instead of refs.
Assign the new records to a local variable agentsArray, then pass that into a function which assigns it to the vue variable 'agents' (its basically a needlessly longer route to the same thing but I thought WHY NOT TRY IT)
Double checked all my uses of 'this' to make sure that I was not accidentally using the wrong this and causing some unknown bug.
Using V-model, but using that doesn't help because I would still have to include the 'agents' inside of the modal in the template.
Using a v-if statement to render the modal HTML in the template only after 'agents' is not an empty array.
Update: Based on a suggestion, removed the function from inside of $(document).ready() inside of the mounted() function.
Template:
<template>
<div class="Q mb-0">
<i class="far fa-question-circle"></i>
<center>
<p class="display-1">{{title}}</p>
{{prefix}} is Representing Themselves Skip This Step.
<div id="searchResults" class="hidden" style="margin-top:5px;">
<a id="searchResultsText" class="SkipStepStyle"></a>
<a
id="viewSearchResults"
style="font-weight: bold;"
class="hidden SkipStepStyle"
v-on:click="displayAgents"
>
View
</a>
</div>
<form class="mt-2 BuyerSellerAgentInfo">
<div class="form-row">
<div class="form-group col-md-6">
<input
ref="NameFirst"
type="text"
:name="prefix+'sAgent_NameFirst'"
placeholder="FIRST NAME"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_NameFirst'].Answer"
>
</div>
<div class="form-group col-md-6">
<input
ref="NameLast"
type="text"
:name="prefix+'sAgent_NameLast'"
placeholder="LAST NAME"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_NameLast'].Answer"
>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input
ref="Email"
type="text"
:name="prefix+'sAgent_Email'"
placeholder="EMAIL ADDRESS"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_Email'].Answer"
>
</div>
<div class="form-group col-md-6">
<input
ref="Phone"
type="text"
:name="prefix+'sAgent_Phone'"
maxlength="14"
placeholder="PHONE #"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_Phone'].Answer"
>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input
ref="Brokerage"
type="text"
:name="prefix+'sAgent_Brokerage'"
placeholder="AGENT'S BROKERAGE"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_Brokerage'].Answer"
>
</div>
<div class="form-group col-md-6">
<input
ref="License"
type="text"
:name="prefix+'sAgent_License'"
placeholder="AGENT'S LICENSE #"
class="AnswerChoice"
:value="currentAnswers[prefix+'sAgent_License'].Answer"
>
</div>
</div>
<input
class="AnswerChoice"
type="hidden"
:name="prefix+'sAgent_ID'"
:value="currentAnswers[prefix+'sAgent_ID'].Answer || '1'"
>
<input
class="AnswerChoice"
type="hidden"
:name="prefix+'sAgent_BrokerageID'"
:value="currentAnswers[prefix+'sAgent_BrokerageID'].Answer || '1'"
>
</form>
</center>
<div v-if="agents.length > 0" class="modal" id="AgentPopup">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-body">
<center>
<h5 class="d-inline-block mb-3">Select {{prefix}}'s Agent:</h5>
</center>
<button v-on:click="displayCategories" type="button" class="close shadow" data-dismiss="modal">×</button>
<ul>
<li v-for="agent in agents">{{ agent.NameFull || agent.NameFirst+' '+agent.NameLast }}</li>
<li class="border-0">{{prefix}}’s agent is not in this list</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
Script:
import _ from 'lodash';
export default {
name: "AgentInformation",
props: {
friendlyIndex: {
type: String,
default: null,
},
title: {
type: String,
default: null,
},
answerChoices:{
type: Array,
default: () => []
},
currentAnswers: {
type: Object,
default: () => {},
},
prefix: {
type: String,
default: '',
},
token: {
type: String,
default: '',
},
},
methods: {
debounceFunction(func,timer){
let vueObject = this;
return _.debounce(()=>{
vueObject[func]();
},timer);
},
displayCategories(){
$('.categories').show();
},
displayAgents(){
$('.categories').hide();
$('#AgentPopup').modal({backdrop:'static',keyboard:false});
},
searchAgent() {
let vueObject = this;
console.log('calling searchAgent()');
let agentSearchRoute = correctVuexRouteURL(vueObject.$store.getters.routeName('search.agent'));
if (!agentSearchRoute) genericError('Agent Search Route Not Found. Error code: a-s-001');
else
{
let dataObject = {
NameFirst: this.$refs.NameFirst.value,
NameLast: this.$refs.NameLast.value,
Email: this.$refs.Email.value,
Phone: this.$refs.Phone.value,
License: this.$refs.License.value,
_token: this.token,
};
console.log(dataObject);
vueObject.$http.post(agentSearchRoute, dataObject).then((r) => {
let status = r.body.status;
if (status == 'success')
{
vueObject.agents = r.body.agents;
let searchResultsContainer = $('#searchResults');
let searchResultsText = $('#searchResultsText');
let viewSearchResultsLink = $('#viewSearchResults');
let agentCount =
vueObject.agents.length;
searchResultsContainer.removeClass('hidden');
if(agentCount > 0)
{
let rText = agentCount > 1 ? 'records' :
'record';
searchResultsText.text(agentCount+' '+rText+'
found.');
viewSearchResultsLink.removeClass('hidden');
}
else
{
if (!viewSearchResultsLink.hasClass('hidden'))
viewSearchResultsLink.addClass('hidden');
searchResultsText.text('No records found.');
}
}
});
}
},
},
data(){
return {
agents: [],
}
},
mounted() {
let vueObject = this;
console.log('mounted');
$(document).ready(function(){
$('#phone').mask('(###)-###-####');
$('.AnswerChoice').on('input', () => {
let searchAgent =
vueObject.debounceFunction('searchAgent',500);
searchAgent();
});
});
}
}
It seems that the issue is the template does not like the 'agents' variable to be inside of it. When I remove the modal container or just the references to 'agents' it works as expected. If I change the variable name it does not solve the issue.
Any thoughts on the solution? Am I missing something blatantly obvious and stupid?!
Edit: Something I forgot to add, I don't think affects this in any way but it is worth mentioning. This component is rendered dynamically inside of the parent.
Rendering the component:
<component
v-for="(component,i) in selectedView"
:is="component['Component']"
v-bind="bindAttributes(component)"
:key="component.ID"
>
</component>
Changing agents will cause the whole template to be re-run. Not just the bits that mention agents, everything in that template will be updated.
When a user types into one of your <input> elements you aren't storing that value anywhere. You've got a :value to poke the value in but you aren't updating it when the value changes. The result will be that when Vue re-renders everything it will jump back to its original value.
You should be able to confirm this by setting the initial values within currentAnswers to be something other than empty. You should find that whenever agents changes it jumps back to those initial values.
The solution is just to ensure that your data is kept in sync with what the user types in. Typically this would be done using v-model but that's a bit tricky in this case because you're using a prop for the values and you shouldn't really be mutating a prop (one-way data flow). Instead you should use events to communicate the required changes up to whichever component owns that data.
Here is a simple test case to demonstrate the issue in isolation:
new Vue({
el: '#app',
data () {
return {
count: 0,
value: 'initial'
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<input :value="value">
<button #click="count++">Click count: {{ count }}</button>
</div>

Update Route Model from Component

I'm new to Ember.js and I'm trying to create an application that mimics Youtube by using their API. Currently I have a route that is responsible for grabbing the initial information from the Youtube Api to populate the page on load. I have a search bar component that is used to gather the input from the user and repopulate the list with results based on the string. The problem is that while I am getting the input from the user my Route model is not refreshing to grab the update data from the api. Below is my code.
Template for my video route video.hbs:
// app/templates/video.hbs
<div class="row">
{{search-bar}}
<div class="row">
<div class="col-md-12">
<hr>
<br>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="row">
{{video-list model=model}}
<div class="col-md-4 pull-right video-container">
{{#if videoId}}
<iframe id="video-player" src="https://www.youtube.com/embed/{{videoId}}"></iframe>
{{else}}
<iframe id="video-player" src="https://www.youtube.com/embed/kEpOF7vUymc"></iframe>
{{/if}}
</div>
</div>
</div>
</div>
</div>
Template for my search bar
// app/templates/components/search-bar.hbs
<div class="col-md-12 col-md-offset-4">
<form class="form-inline">
<div class="form-group" onsubmit="return false">
{{input type="text" class="form-control" value=search id="search" placeholder="Search Videos..."}}
</div>
<button type="submit" {{action "updateSearch"}}class="btn btn-success">Search</button>
</form>
</div>
Component for my search bar
// app/components/search-bar.js
import Ember from 'ember';
export default Ember.Component.extend({
userSearch: "",
actions: {
updateSearch: function() {
this.set("userSearch", this.get("search"));
this.modelFor("videos").reload();
}
}
});
Video Route
// app/routes/video.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var userSearch = this.get("search") === undefined ? "Code" : this.get("search");
this.set("search", userSearch);
var url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q="+ userSearch +"&maxResults=50&key="api key goes here";
return Ember.$.getJSON(url).then(function(data) {
return data.items.filter(function(vid) {
if(vid.id.videoId) {
return vid;
}
});
});
}
});
reload - will not call model hook method, in this case, you can send action to video route and try refresh from there.
EDIT:
Adjusting your code for your use case, Let me know if it's not working or anything wrong in this approach.
app/routes/video.js
Here we are using RSVP.hash function for returning multiple model. I am including userSearch too. Its better to implement query parameters for this use case, but I implemented it without using it.
import Ember from 'ember';
export default Ember.Route.extend({
userSearch: '',
model: function() {
var userSearch = this.get("userSearch") === undefined ? "Code" : this.get("userSearch");
var url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=" + userSearch + "&maxResults=50&key=apikey";
return Ember.RSVP.hash({
videosList: Ember.$.getJSON(url).then(function(data) {
return data.items.filter(function(vid) {
if (vid.id.videoId) {
return vid;
}
});
}),
userSearch: userSearch
});
},
actions: {
refreshRoute(userSearch) {
this.set('userSearch',userSearch);
this.refresh();
},
}
});
app/controllers/viedo.js
It contains refreshRoute function and this will call refreshRoute function available in video route file.
import Ember from 'ember';
export default Ember.Controller.extend({
actions:{
refreshRoute(userSearch){
this.sendAction('refreshRoute',userSearch);
}
}
});
app/templates/video.hbs
1. I am passing userSearch property and refreshRoute action name to search-bar component
2. Accessing videosList using model.videosList
<div class="row">
{{search-bar userSearch=model.userSearch refreshRoute="refreshRoute"}}
<div class="row">
<div class="col-md-12">
<hr>
<br>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="row">
{{video-list model=model.videosList}}
<div class="col-md-4 pull-right video-container">
{{#if videoId}}
<iframe id="video-player" src="https://www.youtube.com/embed/{{videoId}}"></iframe>
{{else}}
<iframe id="video-player" src="https://www.youtube.com/embed/kEpOF7vUymc"></iframe>
{{/if}}
</div>
</div>
</div>
</div>
</div>
app/components/search-bar.js
Here you will get userSearch property as external attributes ie. it will be passed as an argument on including the component.
import Ember from 'ember';
export default Ember.Component.extend({
userSearch:'',//external attributes
actions: {
updateSearch() {
var userSearch = this.get('userSearch');
this.sendAction('refreshRoute',userSearch); //this will call corresponding controller refreshRoute method
}
}
});
app/templates/components/search-bar.hbs
<div class="col-md-12 col-md-offset-4">
<form class="form-inline">
<div class="form-group" onsubmit="return false">
{{input type="text" class="form-control" value=userSearch id="search" placeholder="Search Videos..."}}
</div>
<button type="submit" {{action "updateSearch"}}class="btn btn-success">Search</button>
</form>
</div>

Why would I get, "Uncaught TypeError: Cannot read property 'helpers' of undefined"?

I've got this html in my Meteor project:
<head>
<title>The Dentist Hurt My Fillings</title>
</head>
<body>
<h2>Thirteen Ways of Looking at a Duckbilled Platypus</h2>
<br/>
<br/>
<div class="container">
{{> whizzardBlizzard}}
</div>
</body>
<template name="whizzardBlizzard">
<form>
{{#if firstStep}}
{{> firstStepTemplate}}
{{/if}}
{{#if secondStep}}
{{> secondStepTemplate}}
{{/if}}
{{#if thirdStep}}
{{> thirdStepTemplate}}
{{/if}}
<input type="submit" value="Submit" class="button">
</form>
</template>
<template name="firstStepTemplate">
<h2>Step 1</h2>
</template>
<template name="secondStepTemplate">
<h2>Step 2</h2>
</template>
<template name="thirdStepTemplate">
<h2>Step 3</h2>
</template>
...and this Javascript:
if (Meteor.isClient) {
// stepNum starts at 1
Session.setDefault('stepNum', 1);
Template.whizzardBlizzard.events({
"submit form": function (event) {
//event.preventDefault();
// save the vals to a new document in a collection
Session.set('stepNum', Session.get('stepNum') + 1);
}
});
Template.whizzardBlizard.helpers({
'firstStep': function() {
return (Session.get('stepNum') == 1);
},
'secondStep': function() {
return (Session.get('stepNum') == 2)
},
'thirdStep': function() {
return (Session.get('stepNum') == 3)
}
// . . . etc.
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
When I try to run it, I get, "Uncaught TypeError: Cannot read property 'helpers' of undefined"?
How could that be? Template helpers are a key component of Meteor, and examples of its usage mirror mine.
I have tried it both with and without encasing the helper name (such as "firstStep") in single quotes; that is, I've tried both this:
firstStep: function() {
..and this:
'firstStep': function() {
...while calling it like so:
{{#if firstStep}}
{{> firstStepTemplate}}
{{/if}}
So why is 'helpers' reportedly unreadable?
Blizzard in your helper only has one "z": Blizard

Meteor tutorial doesn't update MongoDB

IM trying out Meteor ToDo-list tutorial but I have problem where I have a form and I should be able to insert the values into the database but it doesn't work. When I hit enter nothing happens.
Here my my html:
<head>
<title>Todo list</title>
</head>
<body>
<div class="container">
<header>
<h1>Tee asjad ära!</h1>
<form class="new-task">
<input type="text" placeholder="Type to add new tasks" />
</form>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
here is the .js file:
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
//see kood jookseb ainult kliendipoolel
Template.body.helpers({
tasks: function () {
return Tasks.find({});
}
});
Template.body.events({
"submit .new-task": function (event) {
var text = event.target.text.value;
Tasks.insert({
text: text,
createdAt: new Date()
});
event.target.text.value = "";
return false;
}
});
}
When I enter values from command line to the database it works fine.
Your input is missing name="text", which is the attribute that lets you access the value through event.target.text.value.
Were you getting an error in the JavaScript console in your browser?

Why is extending an extended view not working in ember.js?

I am trying to create a modal view and have a base class that all modals need and then extending it for more specific functionality.
PlanSource.Modal = Ember.View.extend({
isShowing: false,
hide: function() {
this.set("isShowing", false);
},
close: function() {
this.set("isShowing", false);
},
show: function() {
this.set("isShowing", true);
}
});
PlanSource.AddJobModal = PlanSource.Modal.extend({
templateName: "modals/add_job",
createJob: function() {
var container = $("#new-job-name"),
name = container.val();
if (!name || name == "") return;
var job = PlanSource.Job.createRecord({
"name": name
});
job.save();
container.val("");
this.send("hide");
}
});
I render it with
{{view PlanSource.AddJobModal}}
And have the view template
<a class="button button-green" {{action show target=view}}>+ Add Job</a>
{{#if view.isShowing}}
<div class="modal-wrapper">
<div class="overlay"></div>
<div class="dialog box box-border">
<div class="header">
<p class="title">Enter a job name.</p>
</div>
<div class="body">
<p>Enter a name for your new job.</p>
<input type="text" id="new-job-name" placeholder="Job name">
</div>
<div class="footer">
<div class="buttons">
<a class="button button-blue" {{action createJob target=view}} >Create</a>
<a class="button" {{action close target=view}}>No</a>
</div>
</div>
</div>
</div>
{{/if}}
The problem is that when I click the button on the modal dialog, it gives me an "action createJob" can not be found. Am I extending the objects incorrectly because it works if I put the createJob in the base Modal class.
Fixed
There was an issue somewhere else in my code. The name got copied and so it was redefining it and making the method not exist.

Categories