Vue: Can't set the computed property - javascript

I need some help with computed properties and ajax in vue.
"filterFactories" is a list of factories.
The computed property "filterFactories" creates this list of factories.
Now, I want a new feature:
I have a button for an ajax request which get some new factories.
I want set the computed property "filterFactories" after an ajax request.
Unfortunately nothing happens.
it makes no difference:
1. this.filterFactories = response;
or
2. window.filterFactories = response;
In both cases - nothing happened
Is it possible to update the "filterFactories" after the successfull ajax request?
I have added a larger code snipped
<div id="app">
<div id="filter">
<div class="row">
<div class="col-md-12">
<div class="input-group">
<span class="input-group-addon">Factory</span>
<input type="text" class="form-control" placeholder="Name" aria-describedby="basic-addon1" v-model="searchFactory">
<div class="input-group-btn">
<button class="btn btn-default" type="submit" v-on:click="clearSearchFactory">
<i class="fa fa-times"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<Multiselect
v-model="selectedCapabilities"
:options="allCapabilities"
label="name"
placeholder= "Select capabilities"
track-by="id"
:multiple="true"
></Multiselect>
</div>
<div class="col-xs-12 col-md-6">
<Multiselect
v-model="selectedCountries"
:options="allCountries"
label="name"
placeholder= "Select countries"
track-by="code"
:multiple="true"
></Multiselect>
</div>
</div>
</div>
<!--Modal-->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">{{this.clickedCapability.name}}</h4>
</div>
<div class="modal-body">
<!--......-->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" data-dismiss="modal" #click="filterProperties">OK</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="row" style="height: 35px; margin: 10px">
<button type="button" class="btn btn-outline-dark" v-for="cap in selectedCapabilities" #click="modalCapClicked(cap)" data-toggle="modal" data-target="#myModal"> {{ cap.name }} <i class="fa fa-cogs"></i></button>
</div>
<!--Factories-->
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-3 col-lg-2" id="myCard-wrapper" v-for="factory in this.filterFactories">
<!--a list of factories-->
</div>
</div>
</div>
<script>
window.app = new Vue({
el: '#app',
components: {
Multiselect: window.VueMultiselect.default
},
data() {
return {
//Capabilities
allCapabilities: [], // alle Capabilities aus der Konfiguration die über das Json übermittelt wurden
selectedCapabilities: [], // selektierte Capabilities
clickedCapability: '', // im Modalfenster geöffnete Capability
//Countries
selectedCountries: [], // selektierte Countries
allCountries: [], // alle Countries aus der Json
//Factories
searchFactory: '', // Freitext Suchfeld für Fabriken
factories: [] // angezeigte Fabriken
}
},
computed:{
/* Filtert die Fabriken anhand der Kriterien: Suche-Input, Capabilities, Countries */
filterFactories: function(){
var filteredFactories = [];
var allFilter = [];
allFilter.push(this.filterFactoriesBySearchInput());
allFilter.push(this.filterFactoriesByCaps());
allFilter.push(this.filterFactoriesByCountries());
filteredFactories = allFilter.shift().filter(function(v) {
return allFilter.every(function(a) {
return a.indexOf(v) !== -1;
});
});
return filteredFactories;
}
},
methods: {
/* Filtert anhand der Suchfeld-Eingabe */
filterFactoriesBySearchInput(){
/*filter an return a new list of factories*/
},
/* Filtert anhand der Capabilities */
filterFactoriesByCaps(){
/*filter an return a new list of factories*/
},
/* Filtert anhand der Countries */
filterFactoriesByCountries(){
/*filter an return a new list of factories*/
},
/* Setzt die aktuell im Modal-Fenster geöffnete Capability */
modalCapClicked(cap){
this.clickedCapability = cap;
}
filterProperties(){
axios.post('.....................................')
.then(function (response) {
this.factories = response.data.factoriesJson;
})
.catch(function (error) {
console.log(error);
});
},
clearSearchFactory(){
this.searchFactory = [];
}
},
beforeMount(){
axios.get('.........').then(response => {
this.factories = response.data.elementsJson.factories;
this.allCapabilities = response.data.elementsJson.config.capabilities;
});
axios.get('.......').then(response => {
this.allCountries = response.data;
});
}
})
</script>

Vue uses some magic for computed properties: it scans the function code and will automatically create watchers for the properties it finds within the code. This works well for simple situations but has failed many times for me with loops, map, reduce, etc.
I use multiple workarounds as needed, what is simplest to understand is this: create an artificial property which you reference in the computed prop and then update as needed:
new Vue({
data: {
updated: 0,
},
computed: {
myComputed(): {
// ...
// just a reference to the prop
this.updated;
},
},
methods: {
myAjax(): {
// ...
// modifying the prop will trigger update of myComputed
this.updated++;
},
},
});
Of course you should use names more appropriate to the use cases you have, however i have instances where i just called this property "updateDummy" ;)

Related

Dynamic Modal Using index as ID VueJS

Hoping someone can help.
This is my first time using Vue and I have been asked to create a teams page which pulls data from a list, groups the names by team name in an array and displays them in card format. I have managed to do all this, however when the cards are clicked I'm looking to have all the team members and their role (e.g developer, product manager etc) display in a modal along with the team image
I have used the index as the ID for each card which is working fine but I am unsure as to how to pass this to the modal to display the correct members for each team when the card is clicked. I have tried various methods I've found on here but none seem to relate exactly to what I'm doing and my lack of Vue knowledge is hindering me as I can do this easily with normal HTML/JS.
It's also probably worth mentioning that due to work PC permissions and the fact this is being built on SharePoint I am having to use the Vue CDN and implement this in 1 HTML file rather than using components.
Many Thanks in advance and I hope this makes sense
Here is my HTML:
<div class="container">
<h1 class="page-heading">MEET<span style="color:#ff0000">IN</span>G THE TEAM</h1>
<p class="intro-text">This is a chance for you to tell your Country and the other teams who you are and what you stand for. You’ll
need a team name, team mascot image, who’s in your team and what you want to say to the world.</p>
<br>
<button class="btn btn-outline-light" type="button" role="button" data-target="#create-modal" data-toggle="modal" id="create-team">Create a New Team</button>
<button class="btn btn-outline-light" type="button" role="button" data-target="#create-modal" data-toggle="modal" id="update-team">Update Your Team</button>
<div class="row" id="team-cards">
<div class="col-md-4" v-for="(team, index) in teams" :key="team[0].teamname" v-if="team[0].teamname !== 'No Team'">
<a href="'#teamModal' + index" data-toggle="modal">
<div class="card" v-bind:id="index">
<img v-bind:src="teammate.teamimage" class="card-img-top" v-for="teammate in team" v-if="teammate.teamimage !== null" :key="teammate.teamimage">
<div class="card-body">
<h5 class="card-title"><strong>{{ team[0].teamname }}</strong></h5>
<p class="card-text" v-for="teammate in team" v-if="teammate.teamdescription !== null" :key="teammate.teamdescription">{{ teammate.teamdescription }}</p>
</div>
<div class="card-footer">
<img class="group-image" v-for="teammate in team" v-if="teammate.hackertype !== 'Developer'" :key="teammate.hackertype" src="https://community.global.hsbc/sites/DigiHub/SiteAssets/hub_assetts/Hack%20Teams/Images/red%20group.png">
<!-- <img class="group-image" v-for="teammate in team" v-else-if="teammate.hackertype !== 'Product Manager' || teammate.hackertype == 'Developer'" :key="teammate.hackertype" src="https://community.global.hsbc/sites/DigiHub/SiteAssets/hub_assetts/Hack%20Teams/Images/yellow%20group.png"> -->
<!-- <img class="group-image" v-for="teammate in team" v-if="teammate.hackertype == 'Developer' || teammate.hackertype == 'Product Manager'" :key="teammate.hackertype" src="https://community.global.hsbc/sites/DigiHub/SiteAssets/hub_assetts/Hack%20Teams/Images/green%20group.png"> -->
<a href="https://community.global.hsbc/sites/DigiHub/SitePages/Hack-Chat.aspx" target="_blank">
<img class="chat-image" src="https://community.global.hsbc/sites/DigiHub/SiteAssets/hub_assetts/Hack%20Teams/Images/chat.png">
</a>
</div>
</div>
</a>
</div>
</div>
</div>
<!-- Team Modal -->
<div class="modal fade" v-bind:id="['teamModal'+index]" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><b>{{ team[0].teamname }}</b></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<br>
<div class="modal-body" v-for="teammate in team">
<p>{{ teammate.name }}</p>
<p>{{ teammate.hackertype }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
And my VueJS:
$(function () {
var appTeamCards = new Vue({
el: '#app-teams',
data: {
teams: [],
teamMembers: [],
},
mounted: function() {
this.getTeams();
},
methods: {
getTeams: function() {
var self = this;
$.ajax({
url: '/sites/DigiHub/_api/web/lists/getbytitle(\'global-hackathon-reg\')/items?$orderby=Modified desc',
method: "GET",
headers: {
"Accept": "application/json;odata=verbose"
},
success: function (data) {
var posts = data.d.results;
readData(posts);
console.log(self.teams);
},
});
function readData(data) {
self.teams = groupBy(data, "teamname");
};
function groupBy(collection, property) {
var i = 0, val, index,
values = [], result = [];
for (; i < collection.length; i++) {
val = collection[i][property];
index = values.indexOf(val);
if (index > -1)
result[index].push(collection[i]);
else {
values.push(val);
result.push([collection[i]]);
}
}
return result;
}
},
}
});
$('#update-team').click(function(){
$("#new-member-form").hide();
$("#reg-another-update").show();
$("#update-form").show();
});
$('#create-team').click(function(){
$("#new-member-form").show();
$("#update-form").show();
$("#reg-another-update").hide();
});
$('#reg-another-update').click(function(){
$("#new-member-form").show();
$("#reg-another-update").hide();
$("#update-form").hide();
});
$('#submit-team-btn').click(function(){
$("#update-form").attr("disabled", "disabled");
});
});
It seems I had the right solution but the syntax was slightly wrong. I have made the following amendments and my code now works as expected.
The card anchor reference should be written as follows:
<a :href="'#teamModal' + index" data-toggle="modal">
Modal ID:
<div class="modal fade" :id="'teamModal' + index" v-for="(team, index) in teams" :key="team[0].teamname" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">

Google Maps autocomplete in Bootstrap modal with Vue.js

I have Boostrap modal with <input>. I have implemented Google autocomplete for it with the following well-known trick:
.pac-container {
z-index: 10000 !important;
}
Now I'm struggling to make autocomplete working inside 2nd layer Boostrap Modal. Unfortunately, the z-index trick doesn't work here.
<div class="modal fade" id="editItemModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div id="editItem" class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
Update Address <b>{{selectedItem.properties.NAME}}</b>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label class="sr-only" for="editItem_ADRESS1"></label>
<input v-model="selectedItem.properties.ADRESS1" type="text" class="form-control" id="editItem_ADRESS1" ref="editItem_ADRESS1" placeholder="{{selectedItem.properties.ADRESS1}}">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<a class="btn btn-success btn-ok" #click="save_item()" data-dismiss="modal">Save</a>
</div>
</div>
</div>
</div>
Then comes the Vue object
const editItem = new Vue({
el: "#editItem",
data: {
items: null,
selectedItem: null,
},
methods: {
save_item() {
this.selectedItem = itemsList.selectedItem;
var ip = location.host;
$.ajax({
type: 'POST',
dataType: 'json',
url: 'http://' + ip + '/updateItem',
data: {
command: "edit_item",
item_id: this.selectedItem.id,
adress1: this.selectedItem.properties.ADRESS1
},
success: function (responseData) {
if (responseData.result === false) {
console.log(responseData.result);
}
else {
console.log("successfully updated");
}
},
error: function (error) {
console.log('error', error);
}
}); // end of ajax
} // end od save_item()
} // end of methods
});
Finally, I was able to figure out what was the issue. It turns out that the DOM object is not created yet by Vue when the Google iniAutocomplete() function sets listeners.
In addition, my <input> didn't run the Google geolocate() function. That's how the <input> looks now:
<div class="form-group">
<label class="sr-only" for="edit-item_ADRESS1"></label>
<input id="edit-item_ADRESS1" v-model="selectedItem.properties.ADRESS1" type="text" class="form-control" onFocus="geolocate('edit-item')">
</div>
The next step was to make a minor change in the geolocate() function. I pass action variable onFocus event and use it to determine what DOM object initiated the call.
if (action == "add-item") {
autocomplete_add_item.setBounds(circle.getBounds());
}
if (action == "edit-item") {
// we need to run iniAutocomplete again after the DOM object was finally created by Vue
initAutocomplete();
autocomplete_edit_item.setBounds(circle.getBounds());
}

React - state property is undefined - Why?

I want to load all companies via AJAX request into a state property when the user clicks on the select box.
This is the code:
import React, { Component } from 'react';
import SelectOption from './SelectOption';
class CreateFreightEntryModal extends Component {
constructor(props) {
super(props);
this.state = {
freights: props.freights,
onClose: props.onClose,
onClick: props.onClick,
companies: [],
};
}
loadCompanies(event) {
$.ajax({
type: "POST",
context:this,
dataType: "json",
async: true,
url: "../data/get/json/companies",
data: ({
_token : window.Laravel.csrfToken,
}),
success: function (data) {
var arr = $.map(data, function(el) { return el; });
this.setState({
companies: arr
})
}
});
}
render() {
return (
<div className="modal fade" id="createFreightEntryModal" tabIndex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">New freight entry</h4>
</div>
<div className="modal-body">
<div>
<div>
<form onSubmit={this.add.bind(this)}>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong>Create a new freight entry:</strong>
</div>
</div>
<div className="row">
<div className="col-xs-4 col-sm-4 col-md-4 col-lg-4">
Company
</div>
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8">
<div className="form-group" onClick={this.loadCompanies.bind(this)}>
<select className="selectpicker show-tick form-control" data-live-search="true" data-title="Please select" ref="Firma" required>
{
this.state.companies.map((company)=> {
return (
<SelectOption value={company.Nummer} displayValue={company.Bezeichnung} key={company.id} />
);
})
}
</select>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div className="form-group">
<button type="submit" className="btn btn-success"><span className="glyphicon glyphicon-floppy-disk"></span> Save </button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
);
}
}
export default CreateFreightEntryModal
When I add the componentWillReceiveProps(nextProps) method, I get this error.
This error occurs when the page is loaded and not when I click on the select box!
componentWillReceiveProps(nextProps) {
this.setState({
companies: nextProps.companies,
});
}
TypeError: this.state.companies is undefined
This is the part where the error occurs:
...
this.state.companies.map((company)=> {
...
How can I solve this issue? Thanks for your help in advance.
Using this construct:
componentWillReceiveProps(nextProps) {
this.setState({
companies: nextProps.companies,
});
}
you update state.companies every time the component receives ANY props, even when there are no companies in the props. And when the nextProps don't have companies it is set to undefined.
Let's illustrate it this way:
{
let props = { freights : [ 'a', 'b', 'c' ] }
this.setState({ companies : props.companies })
/* state.companies are now undefined, because props.companies are undefined */
}
Fix:
componentWillReceiveProps(nextProps) {
if( nextProps.companies ){
this.setState({
companies: nextProps.companies,
});
}
}
BTW the problem with success callback scope I have mentioned in a comment may still apply.

Uncaught Error: You cannot apply bindings multiple times to the same element

How could I combine the two models which i have kept for two different purpose. One is to for file upload and the other is for render data from different object.Below is the respective html and JS i tried.
HTML section
<div class="well" data-bind="fileDrag: fileData">
<div class="form-group row">
<div class="col-md-6">
<img style="height: 125px;" class="img-rounded thumb" data-bind="attr: { src: fileData().dataURL }, visible: fileData().dataURL">
<div data-bind="ifnot: fileData().dataURL">
<label class="drag-label">Drag file here</label>
</div>
</div>
<div class="col-md-6">
<input type="file" data-bind="fileInput: fileData, customFileInput: {
buttonClass: 'btn btn-success',
fileNameClass: 'disabled form-control',
onClear: onClear,
}" accept="application/pdf,image/*">
</div>
</div>
</div>
<button class="btn btn-default" data-bind="click: debug">Upload</button>
</div>
<div id="notification" style="display: none;">
<span class="dismiss"><a title="dismiss this notification">X</a></span>
</div>
<!-- Collapsible Panels - START -->
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Plan Details</h3>
</div>
<div class="panel-body">
<span class="glyphicon glyphicon-plus clickable" id="addPlanBtn"></span>
<span class="glyphicon glyphicon-remove clickable" id="removePlanBtn"></span>
<span class="glyphicon glyphicon-edit clickable" id="editPlanBtn"></span>
<table id="docsDataTable" class="table table-striped display" width="100%">
<thead>
<tr>
<th></th>
<th>Contract Document ID</th>
<th>Contract ID</th>
<th>Document Name</th>
<th>File Path</th>
<th>Comments</th>
</tr>
</thead>
</table>
<div class="modal fade" id="notificationDialog" role="dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" \>×</button>
<h4 class="modal-title">Notification</h4>
</div>
<div class="modal-body" id="notificationBody">
</div>
<div class = "modal-footer">
<button type = "button" class = "btn btn-primary" data-dismiss = "modal">
Ok
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="confirmBox" role="dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" \>×</button>
<h4 class="modal-title">Confirmation</h4>
</div>
<div class="modal-body" id="confirmBody">
Selected rows will be made inactive.
</div>
<div class = "modal-footer">
<button type = "button" class = "btn btn-default" data-dismiss = "modal" id="confirmNoBtn">
Cancel
</button>
<button type = "button" class = "btn btn-primary" data-dismiss = "modal" id="confirmYesBtn">
Ok
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Javascript section to bind the data
var dataset;
var docsModel;
var docsTable;
var vasTypes;
$(function(){
var viewModel = {};
viewModel.fileData = ko.observable({
dataURL: ko.observable(),
// base64String: ko.observable(),
});
viewModel.onClear = function(fileData){
if(confirm('Are you sure?')){
fileData.clear && fileData.clear();
}
};
viewModel.debug = function(){
window.viewModel = viewModel;
//console.log(ko.toJSON(viewModel));
fileUpload(viewModel);
debugger;
};
ko.applyBindings(viewModel);
});
$(document).ready(function(){
docsModel = new $.cordys.model({
context: document.getElementById("addPanelForm"),
objectName: "CONTRACT_DOCUMENT",
fields: ["CONTRACT_DOCUMENT_ID" , "CONTRACT_ID" , "DOCUMENT_NAME" , "FILE_PATH" , "COMMENTS"],
defaults: {
namespace: "http://services.vw.com/lpms/1.0/wsapp"
},/*
update: {
method: "UpdatePlanVas"
},*/
read: {
method: "GetContractDocumentObjectsForContractId",
parameters: {
CONTRACT_ID: "CONTRACT_1000"
},
}
});
GetContractDocumentObjectsForContractId();
docsTable = $('#docsDataTable').DataTable({
data: dataset,
columnDefs: [ {
orderable: false,
className: 'select-checkbox',
defaultContent: "",
targets: 0},
{ data: "CONTRACT_DOCUMENT_ID",
targets: 1,
visible: false},
{ data: "CONTRACT_ID",
targets: 2},
{ data: "DOCUMENT_NAME",
targets: 3},
{ data: "COMMENTS",
targets: 5},
{ data: "FILE_PATH",
targets: 4}],
select: {
style: 'multi',
selector: 'td:first-child'
},
order: [[ 1, 'asc' ]],
"searching": false,
"lengthChange": false
});
});
function fileUpload(data){
dataObject=ko.toJS(viewModel);
fileName=dataObject.fileData.file.name;
fileContent=dataObject.fileData.dataURL.split(',')[1];
$.cordys.ajax({
method: "WriteFile",
parameters: {
filename: fileName,
encoded: "true",
data:fileContent
},
namespace:"http://schemas.cordys.com/1.0/ac/FileConnector",
dataType: "* json",
async: false,
success: function(e){
$.notify("Yeah ! File Uploaded", "success");
}
});
}
You're getting the error
You cannot apply bindings multiple times to the same element
Because Knockout only permits one view-model to be bound to a DOM element.
In your case, you need to somehow combine the two view-models into one. While you could simply add the properties from one view-model into the other, perhaps creating a third view-model with a new name so you can continue using the original form of these view-models elsewhere, my suggestion would be to create a new super view-model, and reference the two existing view-models as properties on this new view-model.
At this point I would normally create an example from the code in the OP but in this case, as #Jeroen has pointed out, it's rather difficult to make out what's going on in the OP. As far as I can see, there's only one view-model in there while your question revolves around having two view-models. So the following example is unfortunately very generic.
var ViewModel1 = function() {
var self = this;
self.obs1_1 = ko.observable();
self.obs1_2 = ko.observableArray([]);
// some initialisation stuff
},
ViewModel2 = function() {
var self = this;
self.obs2_1 = ko.observable();
self.obs2_2 = ko.observableArray([]);
// some initialisation stuff
},
SuperViewModel = function() {
var self = this;
self.vm1 = new ViewModel1();
self.vm2 = new ViewModel2();
// some initialisation stuff
};
You would then instantiate and data-bind SuperViewModel, and reference the observables like so
<input type="text" data-bind="textInput: vm1.obs1_1" />
<div data-bind="foreach: vm1.obs1_2">
<span data-bind="html: $data"></span>
</div>
or to make typing a little bit easier
<!-- ko with: vm1 -->
<input type="text" data-bind="textInput: obs1_1" /> <!-- this time without "vm1." -->
<div data-bind="foreach: obs1_2"> <!-- this time without "vm1." -->
<span data-bind="html: $data"></span>
</div>
<!-- /ko -->
You now have a single view-model, SuperViewModel, referencing your unchanged existing view-models. This solution allows you to leave existing JavaScript and views while offering an easy method of data-binding the functionality of multiple view-models inside a single view-model.
It's technically possible to achieve a similar result by doing some referencing at the prototype level, but that could quickly cause complications and the only advantage would be saving you from typing the name of a property.

Binding data to and retrieving data from DOM elements in Meteor 1.2

I am retrieving friend objects via facebook graph API. The idea is to display a list of the names of returned friends, allow user to select one or more friends from this list, and determine the IDs of the friends selected by user once a button is clicked.
So far, I have the following code...
detail.js:
Template.detail.helpers({
...
listOfFriends: function() {
var list = new Array();
if (Meteor.user()) {
list = Meteor.user().profile.listOfFriends;
}
return list;
},
...
});
Template.detail.events({
...
'click .select-friends': function(e) {
e.preventDefault();
// Display modal.
$('#friend_list_modal').modal('show');
Meteor.call('getFacebookFriends', function(err, response) {
if (err) {
alert(JSON.stringify(err));
} else {
if (response.statusCode != 200) {
alert("Error: " + response.statusCode);
}
}
});
},
'click #get_ids_button': function(e) {
e.preventDefault();
var checkedFriendNames = {}, counter = 0;
$("#friend_list li.active").each(function(idx, li) {
checkedFriendNames[counter] = $(li).text();
counter++;
});
// At this point, I have a list of names in checkedFriendNames,
// but I want the IDs and maybe other attributes as well.
$('#friend_list_modal').modal('hide');
}
});
detail.html:
<template name="detail">
<div class="page page-detail">
...
<div class="container">
<div class="btn btn-danger list-friends pull-right" data-toggle="tooltip" title="list friends">
<span class="glyphicon glyphicon-user"></span>
</div>
</div>
</div>
<!-- Modal -->
<div id="friend_list_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">List of Friends</h4>
</div>
<div class="modal-body">
<div>
<div style="max-height: 300px; overflow: auto;">
<ul id="friend_list" class="list-group checked-list-box">
{{#each listOfFriends}}
<li class="list-group-item">{{name}}</li>
{{/each}}
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button id="get_ids_button" type="button" class="btn btn-default">Get IDs</button>
</div>
</div>
</div>
</div>
</template>
server.js:
Meteor.methods({
...
getFacebookFriends: function() {
this.unblock();
var graphResponse = Meteor.http.call("GET", "https://graph.facebook.com/v2.5/me/friends", {
params: {
access_token: Meteor.user().services.facebook.accessToken
}
});
// Save user's list of friends.
Meteor.users.update(Meteor.userId(), {$set: {"profile.listOfFriends": graphResponse.data.data}});
return graphResponse;
},
...
});
What is the best way, in Meteor, to bind the friend object (with id, name, ... attributes) to the DOM and then get these attributes, such as friend ID, back once a selection is made by user?
I'm not sure if Meteor makes this any easier, but you could use data attributes to store extra information about your friend objects in the HTML element.
To save:
<li class="list-group-item" data-friend-id="{{id}}">{{name}}</li>
To retrieve:
var friendId = $(li).data('friendId');

Categories

Resources