Meteor Reactive Tables nested data - mongodb

I have reactive tables working with meteor. I am trying to implement a simple master detail solution. Basically When a row is selected in table 1 the details come up in table 2. When I try and access nested data it shows up in my table as [object: Object]
I am using this package: https://github.com/aslagle/reactive-table#nested-objects-and-arrays
my schema looks like:
ActivityLogs = new Meteor.Collection ('activityLogs');
ActivityLogsSchema = new SimpleSchema({
"aid" : {
type: String,
label: "aid"
},
"logs": {
type: Array
},
"logs.$": {
type: Object
},
"logs.$.stdout": {
type: String
}
});
ActivityLogs.attachSchema( ActivityLogsSchema );
ActivityLogs.attachSchema( ActivityLogsSchema );
in my html:
{{> reactiveTable collection=activityStdout settings=settings1}}
My activityStdout helper:
activityStdout: function() {
var aid = Session.get('selectedAid');
if (aid != null) {
// return ActivityLogs.find({"aid": aid}, {"logs.stdout": 1});
return ActivityLogs.find({"aid": aid});
}
The collection data looks like:
db.activityLogs.find({aid:"7aba0960-b31f-11e7-af97-c56765123d72"}).pretty();
{
"_id" : ObjectId("59e5d080702ec0000df939b6"),
"aid" : "7aba0960-b31f-11e7-af97-c56765123d72",
"logs" : [
{
"stdout" : "performed substitutions on asset dir config-dir/app/config-dir/config-dir-file-2.json"
},
{
"stdout" : "performed substitutions on asset dir config-dir/app/config-dir/config-dir-file-1.json"
},
{
"stdout" : "performed substitutions on asset file undefined"
},
{
"stdout" : "created package package::/sandboxeter./pipe5/app/dev2::pipe5-app-config-Sprint-5.7.4"
}
]
}
in my settinggs1 helper:
settings1: function () {
return {
// collection: activityStdout,
rowsPerPage: 25,
showNavigation: 'auto',
showFilter: false,
fields: [
// {key: 'aid' , label: 'AID' },
{key: 'logs' , label: 'Logs'},
{key: 'stdout' , label: 'output'}
]
};
}
I would like my reactive table to show any key in the logs array. I have been playing with the key above. I have tried: logs.stdout, logs.$.stdout and several other variants with the key.
In the table I get:
The below code works to get the right data but it doent use the reactive table:
<table id="mytable2" class="table table-striped table-bordered table-hover">
<tr>
<td>{{aid}}</td>
</tr>
{{#each activitiesLogList}}
{{#each logs}}
<tr> {{stdout}} </tr>
{{/each}}
{{/each}}
</table>
Any thoughts are appreciated.

After much time with google this is apparently isnt supported with this Meteor Package.
I found this: https://github.com/aslagle/reactive-table/issues/376
The Author Package says:
The only thing that works right now is a specific array index, like
notes.0.remindersActive. There's no way to filter for something
anywhere in the array. Do those work in mongo selectors?

Related

Vuetify datatable reload when performing CRUD operations

I have a simple vuetify datatable that performs CRUD operations. I am using axios and a mongo database. When I add a new item, the information is correctly displayed in the client side and posted in the mongo database. However, I cannot access to updated information of the mongo database, particularly the id that mongo created, unless I reload the webpage. I am newcomer in Vue, please be patient. A simplified version of the problem:
axios
.post('http://localhost:5000/dessert', {
name: this.editedItem.name
})
console.log(this.editedItem.name) // I CAN ACCES WITHOUT PROBLEM
console.log(this.editedItem._id) // I NEED TO RELOAD THE WEBPAGE IN ORDER TO ACCES THIS ELEMENT. THE ID THAT MONGO CREATED.
Vue file:
<template>
<v-data-table
:headers="headers"
:items="desserts"
sort-by="calories"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>My CRUD</v-toolbar-title>
<v-divider
class="mx-4"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on }">
<v-btn color="primary" dark class="mb-2" v-on="on">New Item</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
edit
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</template>
<script>
import axios from 'axios'
export default {
data: () => ({
dialog: false,
headers: [
{
text: 'Dessert (100g serving)',
value: 'name',
},
{ text: 'Calories', value: 'calories' },
{ text: 'Actions', value: 'action', sortable: false },
],
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
},
defaultItem: {
name: '',
calories: 0,
},
}),
mounted() {
this.fetchItems()
},
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
},
},
watch: {
dialog (val) {
val || this.close()
},
},
created () {
this.initialize()
},
methods: {
fetchItems(){
axios
.get('http://localhost:5000/dessert')
.then(response => (this.desserts = response.data.data))
},
editItem (item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.editedID = this.editedItem._id
this.name = this.editedItem.name
this.calories = this.editedItem.calories
this.dialog = true
},
deleteItem (item) {
const index = this.desserts.indexOf(item)
this.deletedItem = Object.assign({}, item)
console.log(this.deletedItem)
this.deletedID = this.deletedItem._id
console.log(this.deletedID)
if (confirm("Do you really want to delete?")) {
axios.delete(`http://localhost:5000/dessert/${this.deletedID}`);
this.desserts.splice(index, 1);
}
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () { // Edit Item
if (this.editedIndex > -1) {
Object.assign(this.desserts[this.editedIndex], this.editedItem)
axios.delete(`http://localhost:5000/dessert/${this.editedItem._id}`)
axios
.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
})
// New Item
} else {
this.desserts.push(this.editedItem)
axios.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
})
}
this.close()
},
},
}
</script>
Python file:
from flask import Flask
from flask import jsonify
from flask import request
from flask_pymongo import PyMongo
from flask_cors import CORS
from bson.objectid import ObjectId
app = Flask(__name__)
#CORS(app)
# instantiate
app.config.from_object(__name__)
# enable CORS
CORS(app, resources={r'/*': {'origins': '*'}})
app.config['MONGO_DBNAME'] = 'restdb'
app.config['MONGO_URI'] = 'mongodb://localhost:27017/restdb'
mongo = PyMongo(app)
#app.route('/dessert', methods=['POST'])
def add_dessert():
dessert = mongo.db.desserts
name = request.json['name']
calories = request.json['calories']
dessert_id = dessert.insert({
'name': name,
'calories': calories
})
new_dessert = dessert.find_one({'_id': dessert_id })
output = {
'name' : new_dessert['name'],
'calories' : new_dessert['calories']
}
return jsonify({'result' : output})
#app.route('/dessert', methods=['GET'])
def get_all_desserts():
dessert = mongo.db.desserts
output = []
for s in dessert.find():
s['_id'] = str(s['_id'])
output.append({'_id' : s['_id'],
'name' : s['name'],
'calories' : s['calories']
})
return jsonify({'data' : output})
#app.route('/dessert/<dessert_id>', methods=['GET'])
def get_one_dessert(dessert_id):
dessert = mongo.db.desserts
s = dessert.find_one({"_id" : ObjectId(dessert_id)})
s['_id'] = str(s['_id'])
if s:
output = {'_id' : s['_id'], 'name' : s['name'], 'calories' : s['calories']}
else:
output = "No such name"
return jsonify({'result' : output})
#app.route('/dessert/<dessert_id>', methods=['DELETE'])
def delete_one_dessert(dessert_id):
dessert = mongo.db.desserts
s = dessert.find_one({"_id" : ObjectId(dessert_id)})
s['_id'] = str(s['_id'])
dessert.remove({"_id" : ObjectId(dessert_id)})
if s:
output = {'_id' : s['_id'], 'name' : s['name'], 'calories' : s['calories']}
else:
output = "No such name"
return jsonify({'result' : output})
if __name__ == '__main__':
app.run(debug=True)
If I understood it correctly, you want to be able to see in the front-end the newly added item, including the generated ID, after posting it to backend, right?
So, you can just simply call the fetchItems() once you finish posting the new item. It will automaticly update the array of the shown items, including the newly added ID.
The ID property is created when the item is added to the database, so it's not possible to have it unless the back-end gives it back to the front-end.
axios.post('http://localhost:5000/dessert', {
name: this.editedItem.name,
calories: this.editedItem.calories
}).then(response => {
this.fetchItems()
})
That means, once finishing the POST, fetchItems() again.

Bootstrap-vue: Auto-select first hardcoded <option> in <b-form-select>

I'm using b-form-select with server-side generated option tags:
<b-form-select :state="errors.has('type') ? false : null"
v-model="type"
v-validate="'required'"
name="type"
plain>
<option value="note" >Note</option>
<option value="reminder" >Reminder</option>
</b-form-select>
When no data is set for this field I want to auto-select the first option in the list.
Is this possible? I have not found how to access the component's options from within my Vue instance.
your v-model should have the value of the first option.
example
<template>
<div>
<b-form-select v-model="selected" :options="options" />
<div class="mt-3">Selected: <strong>{{ selected }}</strong></div>
</div>
</template>
<script>
export default {
data() {
return {
selected: 'a',
options: [
{ value: null, text: 'Please select an option' },
{ value: 'a', text: 'This is First option' },
{ value: 'b', text: 'Selected Option' },
{ value: { C: '3PO' }, text: 'This is an option with object value' },
{ value: 'd', text: 'This one is disabled', disabled: true }
]
}
}
}
</script>
You can trigger this.selected=${firstOptionValue} when no data is set.
what if we don't know what the first option is. The list is generated?
if you have dynamic data, something like this will work.
<template>
<div>
<b-form-select v-model="selected" :options="options" />
<div class="mt-3">Selected: <strong>{{ selected }}</strong></div>
</div>
</template>
<script>
export default {
data() {
return {
selected: [],
options: [],
};
},
mounted: function() {
this.getOptions();
},
methods: {
getOptions() {
//Your logic goes here for data fetch from API
const options = res.data;
this.options = res.data;
this.selected = options[0].fieldName; // Assigns first index of Options to model
return options;
},
},
};
</script>
If your options are stored in a property which is loaded dynamically:
computed property
async computed (using AsyncComputed plugin)
through props, which may change
Then you can #Watch the property to set the first option.
That way the behavior of selecting the first item is separated from data-loading and your code is more understandable.
Example using Typescript and #AsyncComputed
export default class PersonComponent extends Vue {
selectedPersonId: string = undefined;
// ...
// Example method that loads persons data from API
#AsyncComputed()
async persons(): Promise<Person[]> {
return await apiClient.persons.getAll();
}
// Computed property that transforms api data to option list
get personSelectOptions() {
const persons = this.persons as Person[];
return persons.map((person) => ({
text: person.name,
value: person.id
}));
}
// Select the first person in the options list whenever the options change
#Watch('personSelectOptions')
automaticallySelectFirstPerson(persons: {value: string}[]) {
this.selectedPersonId = persons[0].value;
}
}

Ember could not get select value from template to component

I'm struggling with this. I would like to pass a select value from template to component.
Here is my template
<select name="bank" class="form-control" id="sel1" onchange={{action "updateValue" value="bank"}}>
{{#each banks as |bank|}}
<option value={{bank.id}}>{{bank.name}}</option>
{{/each}}
{{log bank.id}}
</select>
And here is my component
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service('store'),
banks: Ember.computed(function() {
return this.get('store').findAll('bank');
}),
didUpdate() {
const banques = this.get('banks');
const hash = [];
banques.forEach(function(banque) {
hash.push(banque.get('name'));
});
Ember.$(".typeahead_2").typeahead({ source: hash });
},
actions: {
expand: function() {
Ember.$('.custom-hide').attr('style', 'display: block');
Ember.$('.custom-display').attr('style', 'display: none');
},
updateValue(selectedValue) {
this.set('bank.id', selectedValue);
},
login() {
console.log(this.get('bank.id'));
}
}
});
And i've got this beautiful error : Property set failed: object in path "bank" could not be found or was destroyed.
Any idea ? Thanks
When you use value attribute then you need to specify correct property name to be retrieved from the first argument(event). in your case you just mentioned bank - which was not found in event object. that's the reason for that error.
onchange={{action "updateValue" value="target.value"}}
inside component
updateValue(selectedValue) {
this.set('bank.id', selectedValue);
},

ag-Grid javaScript, TypeError: rowData is undefined

ag-Grid, following the official demo of javascript but using API like real world over hard-coded data. Note: no jQuery, just use the primitive plain XMLHttpRequest() for ajax.
F12 verified API returns data in the same structure as demo, has children node inside, and gripOptions.rowData is assigned with the returned data.
Tried instantiating rowData inside of gripOptions as
rowData: [], got the same error
Or
rowData: {}, got ReferenceError: rowData is not defined.
HTML:
<script src="/scripts/agGrid/ag-grid.js"></script>
<script src="/scripts/agGrid/myAG.js"></script>
<br />JavaScript ag-Grid
<div id="myGrid" style="height: 200px;" class="ag-fresh"></div>
myAG.js:
var httpApi = new XMLHttpRequest();
var columnDefs = [
{ headerName: "Client Name", field: "ClientName", unSortIcon: true, cellRenderer: "group" },
{ headerName: "Division", field: "Division" },
{ headerName: "Others", field: "Others" }
];
var gridOptions = {
columnDefs: columnDefs,
getNodeChildDetails: getNodeChildDetails
};
function getNodeChildDetails(rowItem) {
if (rowItem.ClientName) {
return {
group: true,
// provide ag-Grid with the children of this group
children: rowItem.children,
// the key is used by the default group cellRenderer
key: rowItem.ClientName
};
} else {
return null;
}
}
// wait for the document to be loaded, otherwise
// ag-Grid will not find the div in the document.
document.addEventListener("DOMContentLoaded", function () {
$.ajax({
type: "GET",
url: "/api/myAG/Tree",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
gridOptions.rowData = data;
var eGridDiv = document.querySelector('#myGrid');
new agGrid.Grid(eGridDiv, gridOptions);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
})
});
Version:
ag-grid = v8.1.0
FireFox = 50.1.0
Error message:
F12 confirms data exists and assigned:
inside of ag-grid.js, the line it complains about but rowData has data:
See this answered post, basically an additional check is needed for the tree.
ag-Grid, try to make Tree Demo work using own data

jquery ui autocomplete js error on keydown

i've included the jquery ui automcomplete plugin into the following structure:
<li class="search">
<input type="text" class="searchfield" name="searchfield" value="Search for Products" />
</li>
my javascript for this input field looks like:
<script type="text/javascript">
function addSearchFieldFunctionality() {
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$('.searchfield').each(function () {
$(this).autocomplete({
source: availableTags,
minLength: 1
}).data("autocomplete")._renderItem = function(ul, item) {
//console.log(item);
var a = $('<a>', {
href: item.value,
text: item.label,
"class" : "mySearchClass"
});
var b = $('<a>', {
href: item.value,
text: "Add", style: "float:right"});
var $li = $('<li></li>', {style:"width:100%"});
return $li.add(a).appendTo(ul);
};
});
}
</script>
I'm loading that function on document ready. for some reason, if a start typing e.g. the first three letters of a item, i get a resultlist. as soon as i push the keydown push button on the keyword, i get the following error in the chrome (latest version) console:
Uncaught TypeError: Cannot read property 'top' of null
a.widget.activate jquery-ui.min.js:12
a.widget.move jquery-ui.min.js:12
a.widget.next jquery-ui.min.js:12
a.widget._move jquery-ui.min.js:12
a.widget._create.element.addClass.attr.attr.bind.bind.d jquery-ui.min.js:12
f.event.dispatch jquery-1.7.1.min.js:3
f.event.add.h.handle.i
i'm using version 1.7.1 of jQuery and Version 1.8.12 of jquery UI
On the demo page of jquery ui autocomplete the keydown works well.
Any ideas what's going wrong with my constellation?
It doesn't make a difference to use remote or local data.
Best regards,
Ramo
I really can make your code working. So I tried to rewrote it in a more simplier way. The problem is render functions only accept strings, not html element. So I add a listener to render the list after its generation (fired on keydown() event).
My thought is you are doing it the wrong way.
why adding another class on those items ? they have already one, so they can be styled.
why transforming them into a nodes ? just add a click() event on them
Could you explain your functional goal ?
// Your list of links
var redirectLinks = {'Ruby': '1234.com', 'Asp': '1235.com'};
function redirect(url) {
// TODO implement window.location=url or whatever you like
if(redirectLinks[url] != undefined) {
alert('redirecting to '+url+' => '+redirectLinks[url]);
}
}
$('.searchfield').each(function () {
$(this).autocomplete(availableTags, {
minLength: 1,
change: function(event, ui) {
console.log('this change event doesnt seem to be fired');
},
select: function(event, ui) {
console.log('this select event doesnt seem to be fired');
}
});
// After the list construction
$(this).keyup(function(e){
if (e.which == 13) { // typing enter validates the input => autocompletechange
console.log('validating input '+$(this).val());
redirect($(this).val());
}
var $list = $('.ac_results:first ul').find('li');
$list.click(function() { // adding an event on suggestion => autocompleteselect
console.log('clicking on suggestion '+$(this).text());
redirect($(this).text());
});
});
});