"Match.Error" message : "Match error: Expected particular constructor" - reactjs

I am new to the Meteor and React Framework, and
I am trying to incorporate 'search' in meteor, and using the package easysearch for it. I am getting an error. The exact wordings of the error are as follows:
match.js:40 Uncaught
errorClass {message: "Match error: Expected particular constructor", path: "", sanitizedError: errorClass, errorType: "Match.Error", stack: "Error: Match error: Expected particular constructo…5a95662d577f9e8a17248e5683161da2f8b114da:3779:14)"}
errorType: "Match.Error"
message: "Match error: Expected particular constructor"
path: ""
I really cannot understand why this error is occurring and how to solve this. I am writing my code below, so that the error can be found exactly.
search.html (the blaze template for the search)
<template name="search">
<div id="search-wrap">
<div>Search page!</div>
<div class="black searchbar">
{{> EasySearch.Input index=index attributes=inputAttributes }}
</div>
{{#EasySearch.IfInputEmpty index=index }}
<div class="padded examples black">For example "Company 1"</div>
{{else}}
{{#if resultsCount}}
<div class="padded count-results black">{{resultsCount}} results found.</div>
{{/if}}
{{/EasySearch.IfInputEmpty}}
{{#EasySearch.IfSearching index=index }}
<div>Searching</div>
{{/EasySearch.IfSearching}}
<ol class="leaderboard">
{{#EasySearch.Each index=index }}
{{> User}}
{{/EasySearch.Each}}
</ol>
{{#EasySearch.IfNoResults index=index }}
<div class="padded no-results black">No results found</div>
{{else}}
{{/EasySearch.IfNoResults}}
{{> EasySearch.Pagination index=index maxPages=10 }}
{{! > EasySearch.LoadMore index=index}}
{{#if showMore}}
{{> EasySearch.Input name="mini-index" index=index attributes=inputAttributes }}
<ul>
{{#EasySearch.Each name="mini-index" index=index}}
<li>{{name}}</li>
{{/EasySearch.Each}}
</ul>
{{/if}}
<!-- Easy Search -->
</div>
<!-- End search -->
--></template>
<template name="User">
<li class="user black {{selected}}" id="user-link">
<span class="name">{{name}}</span>
</li>
</template>
The index.js file (Creating an index on the Collection - companies)
import Companies from "./companies.js";
import { EasySearch } from 'meteor/easy:search';
import { Meteor } from 'meteor/meteor';
import { Mongo } from "meteor/mongo";
export const CompaniesIndex = new EasySearch.Index({
engine: new EasySearch.MongoDB({
sort: function() {
//return based on the latest additions, newest on top
return { createdAt: -1 };
},
//something easy search always asks for
selector: function(searchObject, options, aggregation) {
let selector = this.defaultConfiguration().selector(searchObject, options, aggregation),
//to sort with category.
categoryFilter = options.search.props.categoryFilter;
//search with a category, not really sure what it does, using the easysearch docs.
if(_.isString(categoryFilter) && !_.isEmpty(categoryFilter)) {
selector.category = categoryFilter;
}
return selector;
}
}),
//collection name
collection: Companies,
//fieldname to be searched on
fields: ['name'],
defaultSearchOptions: {
//limit the results size to be 10
limit: 10
},
permission: () => {
return true;
}
});
// export const CompaniesIndex;
The jsx file (Company-search-trial.jsx)
import React from "react";
import Blaze from "meteor/gadicc:blaze-react-component";
import "./pages/search.html";
import Companies from "../api/data/companies.js";
import CompaniesIndex from "../api/data/index.js";
/* A set of controls for the user to select search queries and options.
* For use in the CompanySearchPage.
*/
Template.search.rendered = function() {
$("#search-link").addClass('selected');
$("#profile-link").removeClass('selected');
$("#rankings-link").removeClass('selected');
$("#jokes-link").removeClass('selected');
$("#login-link").removeClass('selected');
}
Template.search.helpers({
inputAttributes: function() {
return { 'class': 'easy-search-input', 'placeholder': 'Start Searching' };
},
players: function() {
return Companies.find({}, { sort: { createdAt: -1 } });
},
selectedName: function() {
var company = CompaniesIndex.config.mongoCollection.findOne({ __originalId: Session.get("selectedCompany") });
return company && company.Name;
},
index: function () {
return CompaniesIndex;
},
resultsCount: function() {
return CompaniesIndex.getComponentDict().get('count');
},
showMore: function() {
return false;
},
renderTmpl: () => Template.renderTemplate
});
Template.User.helpers({
selected: function() {
return Session.equals("selectedCompany", this.__originalId) ? "selected" : '';
},
});
Template.User.events({
'click': function() {
Session.set("selectedCompany", this.__originalId);
}
});
export default class CompanySearchTrial extends React.Component {
render() {
return (
<div className="page CompanySearchTrial">
<Blaze template="search"/>
</div>
);
}
}
Can someone please help me with this. I am really clueless, as to how to go about doing this. Thanks!

The error: Match error: Expected particular constructor" points to a likely cause of an invalid parameter to a function. It also gives us the pointer that it's a missing constructor that's the issue.
The two things in your code that use constructors (ie. are instantiated with new) are EasySearch.Index and EasySearch.MongoDBEngine.
Taking a quick look at the documentation for EasySearch, the correct way to import both of these classes is like so:
import { Index, MongoDBEngine } from 'meteor/easy:search'
// Client and Server
const index = new Index({
...
engine: new MongoDBEngine({
sort: () => { score: 1 },
}),
});
Give the above a go and see if it solves your issue

Related

vue js cannot read property of undefined

I use a v-for loop to list buttons for each category in my array. A function is called on click but it returns an error:
TypeError: Cannot read property 'name' of undefined
Every button is properly displayed in HTML with its name.
v-for loop:
<button :class="{selected: category.exist === true}" v-for="category in categories" :key="category.id" v-on:click="pushFilter(); category.exist = !category.exist">{{ category.name }} {{ category.exist }}</button>
categories data:
export default {
data: function () {
return {
categories: [
{
name: 'name1',
exist: false,
},
{
name: 'name2',
exist: false,
},
],
Method:
methods: {
pushFilter() {
console.log(this.category.name);
},
}
pushFilter() references this.category, but the component has no category prop (at least none shown in question). You're probably trying to access the category iterator of v-for. You could just pass that in the template binding:
<button v-for="category in categories" v-on:click="pushFilter(category)">
And update your method to receive the category argument:
export default {
methods: {
pushFilter(category) {
console.log(category.name)
}
}
}

'Seed' is not defined no-undef. seed.js has IIFE

In React Fullstack by Accomazzo book, in the first chapter they used seed.js as a data source. it has IIFE and they used window object to use it anywhere in the project. but when i created my own react project and followed along the codes it shows an error saying Seed is not defined no-undef
I have tried importing, including the js file in html file but those didn't seem to work
this is the app.js file
import React from 'react';
class PlayerList extends React.Component {
render() {
const player = Seed.players[0]
return (
<div className='ui unstackable items'>
<Player
id={player.id}
name={player.name}
club={player.club}
url={player.url}
votes={player.votes}
submitterAvatarUrl={player.submitterAvatarUrl}
playerImgUrl={player.playerImgUrl}
/>
</div>
)
}
}
this is the seed.js file
window.Seed = (function () {
function generateVoteCount() {
return Math.floor((Math.random() * 50) + 15);
}
const players = [
{
id: 1,
name: 'CR7',
club: 'Juventus',
url: '#',
votes: generateVoteCount(),
submitterAvatarUrl: 'images/avatars/profile-pic.jpeg',
playerImgUrl: 'images/players/CR7.jpg',
},
{
id: 2,
name: 'LM10',
club: 'Barcelona',
url: '#',
votes: generateVoteCount(),
submitterAvatarUrl: 'images/avatars/kristy.png',
playerImgUrl: 'images/players/LM10.jp',
},
{
id: 3,
name: 'MS10',
club: 'Liverpool',
url: '#',
votes: generateVoteCount(),
submitterAvatarUrl: 'images/avatars/veronika.jpg',
playerImgUrl: 'images/players/MS10.jpg',
},
];
return { players: players };
}());
Here you can find a working example, just import your seed file. Basically, you only need to import 'seed' and to avoid errors add window.Seed
https://codesandbox.io/s/stackoverflow-import-iife-5mojf

how to access an object element in a vue js list (nuxt)

I have an array of object like that :
{ id: 33617,
datePublication: 1532266465,
dateUpdate: 1532266574,
headline: 'An headline title here',
images: [ [Object] ] },
{ id: 33614,
datePublication: 1532265771,
dateUpdate: 1532271769,
headline: 'another super headline article',
images: [ [Object], [Object], [Object], [Object], [Object] ] }
so i iterate that way on my vue js template :
<v-flex v-for="(ip, i) in ips" :key="i" xs12 sm6 >
<v-card>
<v-card-media
:src="ip.images[0].url"
height="200px"
/>
<v-card-title primary-title>
<div>
<h3 class="headline mb-0">{{ ip.headline }}</h3>
<div>Located two hours south of Sydney in the <br>Southern Highlands of New South Wales, ...</div>
</div>
</v-card-title>
<v-card-actions>
<v-btn flat color="orange">Share</v-btn>
<v-btn flat color="orange">Explore</v-btn>
</v-card-actions>
</v-card>
</v-flex>
And here is my associated script :
var api = "https://my api.org/news/fr-FR/2018-25-07";
export default {
data() {
return {};
},
layout: "no-live",
async asyncData({ app }) {
const ips = await app.$axios.$get(api);
console.log(ips);
return { ips };
}
};
Each object in the images array should return me an id and an url, so i want to take that url as a source in my image but i have this error : Cannot read property '0' of undefined
it seems i need to do another loop over the images array, is there a proper way to do that with vue JS ?
if you have any objects in ips that doesn't have images, you would get that error,
you can try to add a conditional to not have an error in rendering
<v-card-media
v-if="ip.images && ip.images[0]"
:src="ip.images[0].url"
height="200px"
/>
I usually add a <pre>{{ip}}</pre> in these cases to see what's going on.
Try put v-if="ip.images" on v-card-media component. You will assure the images are not empty and loaded.

Angular2: How do you properly bind to nested data?

I have a component I'm going to use as a shell for multiple choice questions to load into. Here's how the component is set up so far
component
import { Component } from '#angular/core';
export class Answers{
id: string;
answer: string;
}
const answers: Answers[] = [
{
id: 'exp01q',
answer: 'Its fine as is.'
},
{
id: 'exp02q',
answer: 'I want to make minor adjustments.'
},
{
id: 'exp03q',
answer: 'I want to change my image'
},
{
id: 'exp04q',
answer: 'Ive never wanted to use a particular image until now.'
}
];
#Component({
moduleId: module.id,
selector: 'multi-radio-btn',
templateUrl: 'multi-rad-btn.component.html',
styleUrls: ['multi-rad-btn.component.css']
})
export class MultiRadioBtnShell {
question = 'How do you feel about your current image?';
id = 'exp-img-q';
name = 'exp-ques1';
ans = answers;
}
HTML Template
<h3>radio button shell</h3>
<div class="row justify-content-center">
<fieldset [attr.id]='id' class="card col-8 justify-content-center">
<label class="ques-title">
{{question}}
</label>
<div class="row answer-row-section justify-content-center">
<div *ngFor="let answers of ans" class="col col-12 answer-row justify-content-center">
<div class="col justify-content-center">
<input type="radio"
[attr.id]="answers.id"
[attr.name]="name"
[attr.value]="answers.answer" hidden />
<label [attr.for]="answers.id" class="col ques-ans-title" style="background-color: #4b73a0;">
{{answers.answer}}
</label>
</div>
</div>
</div>
</fieldset>
</div>
The reason it's set up like this now is because the way I was trying to do it at first wasn't working so I went to the Tour of Heroes tutorial to follow along with how they loaded all the heroes. The problem was coming from answer not being defined. So I rigged that part up the same way they did the heroes just for the sake of doing something I'm able to follow just to be sure I get the mechanics of how things load.
The original way I tried to do it was with this
// I had this right above the component
export class ExpQ{
question: string;
id: string;
name: string;
answers:[
{
id: string;
answer: string;
}
]
}
// I had this in the component's class
export const expq: ExpQ[] = [
{
question: 'How do you feel about your current image?',
id: 'exp-img-q',
name: 'exp-ques1',
answers:[
{
id: 'exp01q',
answer: 'Its fine as is.'
},
{
id: 'exp02q',
answer: 'I want to make minor adjustments.'
},
{
id: 'exp03q',
answer: 'I want to change my image'
},
{
id: 'exp04q',
answer: 'Ive never wanted to use a particular image until now.'
}
]
}
]
I was calling it in the html with
{{expq.question}}, {{expq.name}}, {{expq.answers.id}}, {{expq.answers.answer}}, etc.
at first with just loading the question it worked fine, but as I got to the answers: part it started breaking. I came across this https://scotch.io/tutorials/using-angular-2s-model-driven-forms-with-formgroup-and-formcontrol and seen the syntax for the addresses: part was pretty much the same as how I needed to structure my data. So I remade everything to resemble that. I still had no luck getting it to work.
Ultimately I'm going to be sending the questions through the parent component with #input and #output as well as a couple other tricks I came across. But before I can even think about that I need to get a handle on how to put the data all into one source so that it properly reads the nested bits of data. All the examples I come across are simple single tier bits of data, so I'm not sure on the syntax I need to use. How can I make this work?
You can define your model like so:
export interface Answer {
id: string;
answer: string;
}
export interface Question {
id: string;
name: string;
question: string;
answers: Answer[];
}
Then your component could have this to test
question1: Question = {
id: 'q1',
name: 'q1',
question: 'Does TypeScript rule?',
answers: [
{ id: 'a1', answer: 'Yes' },
{ id: 'a2', answer: 'Of Course' },
{ id: 'a3', answer: 'Duh' }
]
};
Of course the names don't have to be the same but I think this gives you a better idea of how to model nested data.
Then to display it you will need to iterate over nested structures. Look up the *ngFor directive. You will want to iterate over your answers in this case. Ex:
<div *ngFor="let answer of question1.answers">
{{answer.id}} - {{answer.answer}}
</div>
Need to flatten the objects,
Params :
Objects : at least n>0 array off JSON objects (dose not matter is circular)
target : {}
path : ""
Note : make sure the Objects Array passed in is n>0 at least
flatten(objects, target, path) {
let me = this;
let retArray = [];
for(let x=0; x < objects.length; x++) {
let object = objects[x];
path = path || '';
target={};
target = me.flattenHelper(object, target, path);
retArray.push(target);
}
return retArray;}
..
flattenHelper(object, target, path){
let me = this;
Object.keys(object).forEach(function (key) {
console.log("key : "+ key + " : object : " + (object[key] && typeof object[key] === 'object') + " path : " + path);
if (object[key] && typeof object[key] === 'object') {
me.flattenHelper(object[key], target, path + key);
}
target[path + key] = object[key];
console.log(target);
});
return target;}

AngularJS - Design pattern for returning a subset of data from a service... and binding to the set?

I have a service that manages a set of data. The service is polite enough to provide options to return a subset of said data based on (whatever logic, in this example it's simply going to look for a specific data attribute value). The service will return an array of matches. In my view, I want to bind to this set of matches. However, because the service returns a new array object each time the filter function is called, that doesn't work. My view is bound to the previously returned array object.
Try this fiddle:
http://jsfiddle.net/FdWeK/1/
var app = angular.module('app', []);
app.factory('MrData', function() {
var allData = [
{name: 'Adam', type: 'boy'},
{name: 'Kassidy', type: 'girl'},
{name: 'Justin', type: 'boy'},
{name: 'Chloe', type: 'cat'},
{name: 'D The P', type: 'dog'},
];
return {
add: function(thing) {
allData.push(thing);
},
fetchAll: function() {
return allData;
},
fetchForType: function(type) {
var some = [];
for (var i = 0; i < allData.length; i++) {
if (allData[i].type == type)
some.push(allData[i]);
}
return some;
}
}
});
app.controller('SomeCtrl', function($scope, MrData) {
$scope.showSome = MrData.fetchForType('boy');
$scope.showAll = MrData.fetchAll();
$scope.addBoy = function() {
MrData.add({name: 'TED!', type: 'boy'});
}
$scope.addOther = function() {
MrData.add({name: 'Other', type: 'Other'});
}
});
and the view:
<div ng-app="app">
<div ng-controller="SomeCtrl">
<button ng-click="addBoy()">Add Boy</button>
<button ng-click="addOther()">Add Other</button>
<h2>Boys</h2>
<ol>
<li ng-repeat="thing in showSome">
{{ thing.type }}
{{ thing.name }}
</li>
</ol>
<h2>All</h2>
<ol>
<li ng-repeat="thing in showAll">
{{ thing.type }}
{{ thing.name }}
</li>
</ol>
</div>
</div>
You can see that the list of boys is not updated when you click Add Boy. And I understand why- but I don't understand how to fix this! Must be a simple design pattern or feature that I just don't know about, or can't figure out on my own.
Thanks you in advance,
Adam
You obviously understand what is broken, that your view is bound to two different lists. However, the problem is that you are filtering the model, when you really should be filtering the view. This way you always stay bound to a single list, and the view manages how that list is presented to the user.
What you should be using is a filter https://docs.angularjs.org/api/ng/filter/filter
For example, this simple ng-repeat should work:
<li ng-repeat="thing in showAll | filter:{type: 'boy'}">
Also updated fiddle
You need to reapply the fiters because the bound arrays have not changed when you call addBoy() or addOther().
One way around this is to call the filter after each call:
function applyFilter(){
$scope.showSome = MrData.fetchForType('boy');
$scope.showAll = MrData.fetchAll();
}
$scope.addBoy = function() {
MrData.add({name: 'TED!', type: 'boy'});
applyFilter();
}
$scope.addOther = function() {
MrData.add({name: 'Other', type: 'Other'});
applyFilter();
}
I would show how it could be done with angular filters but it seems someone already has.

Resources