I have tables "Category & SubCategory". CategoryID is "foreign key" in SubCategory Table. I need to fetch all the Category and related SubCategories and bind in an Accordion list.
This is my code:
<div id="accordian" data-bind="foreach:Categories">
<div class="panel-heading">
<h4 class="panel-title"><span data-bind="text: CategoryName"></span></h4>
</div>
<div class="panel-body">
<ul data-bind="foreach:SubCategories">
<li><span data-bind="text: SubCategoryName" ></span></li>
</ul>
</div>
</div>
Please help me on this.
I have created a demo with fake response which should be provide via you endpoint. There were also missing self declaration in order to have object reference. So simply add following in begging of you VM.
var self = this;
I have also created fake method which simulate fake data array the same as you service should to provide.
function ajaxHelperFakeResponse(callback) {
var response = [{
CategoryName: 'catgeory1',
SubCategories: [{
SubCategoryName: 'SubCategoryName1'
}, {
SubCategoryName: 'SubCategoryName2'
}]
}];
callback(response);
}
You can see whole solution in my fiddler http://jsfiddle.net/jakethashi/ow785abr/
Related
This are the event listeners for my two templates,specified in events.js
// event listeners on the addSiteForm template
Template.addCommentForm.events({
// this runs when they click the add button... you need to compete it
'click .js-add-comment':function(event){
var comment_text = $('#comment_input').val();// get the form value using jquery...
var user = 'anonymous person';
// the 'this' variable contains
// the data that this template is displaying
// which is the Website item
var site = this;
if (Meteor.user()){
user = Meteor.user().emails[0].address
}
var comment = {"text":comment_text,
"siteId":site._id,
"createdOn":new Date(),
"createdBy":user};// create a simple object to insert to the collectoin
Comments.insert(comment);
console.log("events start");
console.log("totla coomets are "+Comments.find({}).count()+"\n");
console.log("commenst in site "+Comments.find({siteId:site._id}).count()+"\n");
console.log("site id is "+site._id);
console.log("events end");
return false;
}
});
// event listeners on the addSiteForm template
Template.addSiteForm.events({
// this runs when they click the add button... you need to compete it
'click .js-add-site':function(event){
var url = $('#url_input').val();// get the form value using jquery...
var user = 'anonymous person';
if (Meteor.user()){
user = Meteor.user().emails[0].address
}
var site = {"url":url,
"createdOn":new Date(),
"createdBy":user};// create a simple object to insert to the collectoin
Websites.insert(site);
return false;
}
});
The router function is specified in router.js
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// the main route. showing the list of sites.
Router.route('/', function () {
this.render('siteList');
});
// this route is for the discussion page for a site
Router.route('/discussSite/:_id', function () {
var siteId = this.params._id;
site = Websites.findOne({_id:siteId});
this.render('discussSite', {data:site});
});
the main.js contain the collections
// shared code
Websites = new Mongo.Collection("websites");
Comments = new Mongo.Collection("comments");
helpers is
// this helper gets the data from the collection for the site-list Template
Template.siteList.helpers({
'all_websites':function(){
return Websites.find({});
},
'safer_email':function(email){
if (email.indexOf('#')!=-1){// we have an email
return email.split('#')[0];
}
else{// probably anonymouse.
return email;
}
}
});
Template.discussSite.helpers({
'comments':function(siteId){
//console.log("helper comments "+this.site);
// complete the code here so that it reruns
console.log(this.params.site._id);
return Comments.find({siteId:siteId});
// all the comments with a siteId equal to siteId.
}
});
the html file is in main.html.here it first displays the websites available in the webpage.On clicking this the user is routed to a new page.Here the user can add comments for the website
<head>
<title>week_4_peer_assessment</title>
</head>
<body>
</body>
<!-- this is the template that iron:router renders every time -->
<template name="ApplicationLayout">
<div class="container">
Home
{{>loginButtons}}
<h1>SiteAce - discuss your favourite websites</h1>
<!-- iron router will select what to render in place of yield-->
{{> yield }}
</div>
</template>
<template name="discussSite">
<h3>Discussing: {{url}} </h3>
{{> addCommentForm}}
<!-- write some code here that iterates through the comments and displays
the comment text and the author -->
<!-- clue - you have already written the 'comments' helper function -->
<ul>
{{#each comments}}
<li>{{text}} (added by {{safer_email createdBy}})
</li>
{{/each}}
</ul>
</template>
<template name="addCommentForm">
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" id="comment_input" class="form-control" placeholder="Enter your comment">
<input type="hidden" id="site_id" class="form-control" placeholder="Enter your comment">
<span class="input-group-btn">
<button class="btn btn-default js-add-comment" type="submit">Add!</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</div>
</template>
<template name="siteList">
Fill in the form and click submit to add a site:
{{>addSiteForm}}
<h3>Sites you have added:</h3>
<ul>
{{#each all_websites}}
<li>{{url}} (added by {{safer_email createdBy}})
<br/>discuss
<br/>visit site
</li>
{{/each}}
</ul>
</template>
<template name="addSiteForm">
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" id="url_input" class="form-control" placeholder="Enter website URL...">
<span class="input-group-btn">
<button class="btn btn-default js-add-site" type="submit">Add!</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</div>
</template>
The starup.js contains the start up code run by the server
Meteor.startup(function(){
if (!Websites.findOne()){// nothing in the database yet
var site = {"url":"http://www.google.com",
"createdOn":new Date(),
"createdBy":"Michael"};// create a simple object to insert to the collectoin
Websites.insert(site);
site = {"url":"http://www.yeeking.net",
"createdOn":new Date(),
"createdBy":"Janet"};// create a simple object to insert to the collectoin
Websites.insert(site);
site = {"url":"http://www.coursera.org",
"createdOn":new Date(),
"createdBy":"Jose"};// create a simple object to insert to the collectoin
Websites.insert(site);
}
});
This is what your template should be: (I removed safer_email. It was erroring. Not sure if it was something else you were working on.)
<template name="discussSite">
<h3>Discussing: {{url}} </h3>
{{> addCommentForm}}
<!-- write some code here that iterates through the comments and displays
the comment text and the author -->
<!-- clue - you have already written the 'comments' helper function -->
<ul>
{{#each comments}}
<li>{{text}} (added by {{createdBy}})</li>
{{/each}}
</ul>
</template>
helper:
your helper function is designed to take in siteId as an input, yet you are not passing one to it.
this._id or Router.current().params._id will get you the siteId that you are looking for. You'd used this.site._id which used to error out as this.site was not valid.You can further see what the this object contains by doing a console.log(this). That would have helped you sort this out faster.
'comments':function(){
console.log("site id in helper is ",this._id); // or Router.current().params._id;
console.log(Comments.find({siteId:this._id}).fetch());
return Comments.find({siteId:this._id});
}
I am doing a Meteor project for educational purpose. It's a blog with a post list page and a post detail page where logged-in users can add comments. Disclaimer: In the project I cannot use aldeed-simple schema and I won't use pub/sub methods. I am using iron-router and the accounts-password package for user authentication.
Step 1
I' ve set a basic application layout and a basic routing:
Router.route('/posts', function () {
this.render('navbar', {
to:"navbar"
});
this.render('post_list', {
to:"main"
});
});
Router.route('/post/:_id', function () {
this.render('navbar', {
to:"navbar"
});
this.render('post_detail', {
to:"main",
data:function(){
return Posts.findOne({_id:this.params._id})
}
});
Step 2
I ve created a post detail view which include a comment form. Find the code below:
<template name="post_detail">
<div class="container">
<p>Title: {{title}}</p>
<h5>Description: </h5>
<p>{{description}}</p>
<h4>Comments</h4>
<ul>
{{#each comment in comments}}
<li>{{comment}}</li>
{{/each}}
</ul>
{{>comments}}
</div>
</template>
<!-- Comments -->
<template name="comments">
<form class="js-add-comment-form">
<input type="text" id="comment" class="form-control"/>
<button type="submit" class="btn btn-default js-add-comment">Add a comment</button>
</form>
Step 3
I've added an event helper to the comment template in order to add user comments. The form takes the comment and the user Id. See code below:
Template.comments.events({
'submit .js-add-comment-form': function(event) {
event.preventDefault();
var comment = event.target.comment.value;
console.log(comment);
var user = Meteor.userId();
var email = Meteor.user().emails[0].address;
console.log(email)
console.log(user);
if (Meteor.user()){
Posts.update(
{_id: this._id},
{$push: {comments: {comments:comment, user}}});
event.target.comment.value = "";
}
}
});
Find below a post detail view in the browser:
I've inserted some comments but I cannot display it on the page. Instead I get and Object object for each comment and user id. If I go to the console this is my object:
How can I access those objects and display those field values(comments and user) on the page? Any help ? Thanks Sandro.
This is happening because {{comment}} is an object itself. Here's some sample code to get the message property showing:
<template name="post_detail">
<div class="container">
<p>Title: {{title}}</p>
<h5>Description: </h5>
<p>{{description}}</p>
<h4>Comments</h4>
<ul>
{{#each comment in comments}}
<li>{{comment.comments}} | {{emailForUser comment.user}}</li>
{{/each}}
</ul>
{{>comments}}
</div>
</template>
If you want to get the user's email address as per my example above, you'll want to add a template helper:
Template.post_detail.helpers({
emailForUser( id ) {
var user = Meteor.users.findOne({_id: id});
if( user ) {
return user.emails[0].address;
}
}
});
I have singlePost with postId. In each singlePost, I list comment.
Autoform for comment:
{{#autoForm id="updateCommentArray" type="update-pushArray" collection=Collections.Posts doc=commentDoc scope="comment" template="semanticUI"}}
{{> afQuickField name="content"}}
<div class="form-group">
<button type="submit" class="ui positive button">Submit</button>
<button type="reset" class="ui negative button">Reset</button>
</div>
{{/autoForm}}
What Autoform provide is to use scope to attach new array into specified field. For example, when I use scope comment.0.reply, that reply will attach to first array of comment. When I use scope comment.1.reply, that reply will attach to second array of comment. Etc
How to make it dynamic? What I thought is to use commentId, but how?
Thank you
I think it works like this:
The scope defines to which array inside a document the form is adding.
The doc is the document where the form shall add data to the scope.
For example (i did not test this, but it is similar to code i used):
javascript:
CommentsSchema = new SimpleSchema({
comment:{
type:String,
autoform:{
rows: 2
}
}
});
PostSchema = new SimpleSchema ({
content:{
type:String,
autoform: {
rows: 2
}
},
comments:{
type:[CommentsSchema]
}
});
template:
{{#each posts}}
{{#autoForm id=this._id type="update-pushArray" collection='Posts' doc=this scope="comment" template="semanticUI"}}
{{> afQuickField name="content"}}
<div class="form-group">
<button type="submit" class="ui positive button">Submit</button>
<button type="reset" class="ui negative button">Reset</button>
</div>
{{/autoForm}}
{{/each}}
I have a template for a collection of Groups. Each group document contains an array with each element containing a number and a boolean. The template is laid out as follows:
<template name="group">
<li class="{{#if checked}}checked{{/if}} groupBox">
<button class="delete">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked-group" />
<span class="text">{{name}}</span>
<ul>
{{#each this.numbers}}
<li class="checked">
<button class="deleteNumber">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked-number" />
<span class="text">{{number}}</span>
</li>
{{/each}}
</ul>
<form class="new-number">
<input type="text" name="number" placeholder="Type to add a number to this group" />
</form>
</li>
</template>
An example group document is laid out as such:
{
"_id": "pSpNcJKDPhRtGhtov",
"name": "Sample Group",
"createdAt": "2015-06-27T00:45:39.137Z",
"owner": "t2ELweZsZqJXNQqdZ",
"username": "brodan",
"checked": false,
"numbers": [
{
"number": "1234567",
"checked": true
}
]
}
I need to be able to remove a specific number from the numbers array when I click on the deleteNumber button. I have the following javascript and can't figure out how to get the parent context to use in the MongoDB query:
Template.group.events({
"click .deleteNumber": function () {
Meteor.call("deleteNumber");
}
});
Meteor.methods({
deleteNumber: function (numbers) {
//Need to delete number from group via MongoDB query here.
}
});
I know that I can use the $pull operator in my MongoDB query in the deleteNumber method, but in order to get the right group, I need to have the parent data from the number in the template.
Solved thanks to xamfoo's answer.
The working deleteNumber method is now set up like this:
deleteNumber: function (groupId, number) {
Groups.update(groupId, {$pull: {numbers: {"number": number}}});
}
You should be able to get the context via this in the event handler and group data with Template.instance().data:
Template.group.events({
"click .deleteNumber": function () {
var data = Template.instance().data;
Meteor.call("deleteNumber", data._id, this.number);
}
});
In this case, parameters of deleteNumber are:
Meteor.methods({
deleteNumber: function (groupId, number) {
//Need to delete number from group via MongoDB query here.
}
});
You can also access the data context with Template.currentData() or Template.parentData().
I'm a beginner in the AngularJs and MongoDb world (i started learning today!!)
Actually i'm trying to do something very basic : Display a list of record, with an add button and a edit link with each record.
I'm using this lib https://github.com/pkozlowski-opensource/angularjs-mongolab to connect to mongoweb.
Actually my data is displayed, when i try to add a record it works, but the problem is when i try to display the edit form!
Here is my index.html file, in which i display the data with a form to add a record and with the edit links :
<div ng-controller="AppCtrl">
<ul>
<li ng-repeat="team in teams">
{{team.name}}
{{team.description}}
edit
</li>
</ul>
<form ng-submit="addTeam()">
<input type="text" ng-model="team.name" size="30" placeholder="add new team here">
<input type="text" ng-model="team.description" size="30" placeholder="add new team here">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
And here is my edit.html code, which displays an edit form :
<div ng-controller="EditCtrl">
<form ng-submit="editTeam()">
<input type="text" name="name" ng-model="team.name" size="30" placeholder="edit team here">
<input type="text" name="description" ng-model="team.description" size="30" placeholder="edit team here">
<input class="btn-primary" type="submit" value="validate edit">
</form>
</div>
And finally my js code:
var app = angular.module('app', ['mongolabResource']);
app.constant('API_KEY', '____________________________');
app.constant('DB_NAME', 'groups');
app.factory('Teams', function ($mongolabResource) {
return $mongolabResource('teams');
});
app.controller('AppCtrl', function ($scope, Teams) {
$scope.teams = Teams.query();
$scope.addTeam = function() {
varteam = {
name: $scope.team.name,
description: $scope.team.description
};
$scope.teams.push(varteam);
Teams.save($scope.team);
$scope.team.name = '';
$scope.team.description = '';
};
});
app.controller('EditCtrl', function ($scope, Teams) {
//????????
});
My AppCtrl works perfecty, it displays the data w add records perfectly.
Now i want to add the js code for the edit, but i don't even know form where to start ? how do i a get the id parameter in the url ? how do i tell the view to fill out the form fields from the values from the database ? And finally how do i update the databse.
I know that i asked a lot of question but i'm really lost! thank you
There are of course many possible solutions.
One solution is to use angularjs routing. See http://docs.angularjs.org/tutorial/step_07 for a tutorial.
Basically replace your ul list with something like:
<ul>
<li ng-repeat="team in teams">
{{team.name}}
{{team.description}}
edit
</li>
</ul>
Then you can create a route that responde to your url:
yourApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/teams', {
templateUrl: 'partials/team-list.html',
controller: 'TeamListCtrl'
}).
when('/teams/:teamId', {
templateUrl: 'partials/team-detail.html',
controller: 'TeamDetailCtrl'
}).
otherwise({
redirectTo: '/teams'
});
}]);
In this way from the detail controller (that will replace your EditCtrl) you can access the id parameter using: $routeParams.teamId
Anyway I suggest to study well all the tutorials for a better overview.