How to put content under certain headings?
I have two methods to load data
first axios.get('/api/get/headers')
second axios.get('api/get/contents')
I have no idea how correctly this will be done, given that the headers can be different and the content is correspondingly too
table example
<thead>
<tr>
<th v-for="header in headers" :data-key="header.name">{{ header.title }}</th>
</tr>
</thead>
<tbody>
<tr v-for="content in contents">
<td :data-key="content.name"> {{content.title}}</td>
</tr>
</tbody>
The global problem is that the content can be an array
Here's a one-page example of a way to use axios with Vue.js to get some simple content on the page. Does this help you get started?
<html>
<head>
<script src="http://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<div class="header">{{headerText}}</div>
<div class="content">{{contentText}}</div>
</div>
</body>
<script>
var vm = new Vue({
el: "#app",
data: function () {
return {
headerText: '',
contentText: ''
}
},
mounted: function () {
axios.get("/api/get/headers").then(response => {
this.headerText = response;
})
.catch(err => {
console.log(err);
});
axios.get("/api/get/content").then(response => {
this.contentText = response;
})
.catch(err => {
console.log(err);
});
}
});
</script>
</html>
Update:
For your specific example, could you go through the list of headers again and match the name property with the content name property, and then display the content? Like so:
<thead>
<tr>
<th v-for="header in headers" :data-key="header.name">{{ header.title }}</th>
</tr>
</thead>
<tbody>
<tr v-for="header in headers">
<td :data-key="header.name"> {{getContent(header.name).title}}</td>
</tr>
</tbody>
<script>
...
methods: {
getContent(name) {
for (var index in this.contents) {
if (this.contents[index].name === name) {
return this.contents[index];
}
}
}
}
...
</script>
Related
I am working on a small application that displays a "users" JSON in an HTML5 table. It uses Bootstrap 3 and Vue.
The code I have so far:
var app = new Vue({
el: '#app',
data () {
return {
users: null,
loading: true,
errored: false
}
},
mounted () {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
this.users = response.data
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
}
});
Vue.filter('lowercase', function (value) {
return value.toLowerCase();
})
<script src="https://unpkg.com/axios#0.18.0/dist/axios.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="container">
<div class="panel panel-default table-container">
<div class="panel-heading">Users</div>
<div class="panel-body">
<table class="table table-striped table-bordered" id="dataTable">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>City</th>
</tr>
</thead>
<tbody>
<tr v-for="(user, index) in users">
<td>{{index + 1}}</td>
<td>{{user.name}}</td>
<td>{{user.email | lowercase}}</td>
<td>{{user.address.city}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
I want to turn the email addresses in the table into email links: <a :href="mailto:user.email | lowercase">{{user.email | lowercase}}.
The line above results in an Error compiling template and prevents the rendering of the table (even though <a :href="user.email | lowercase">{{user.email | lowercase}}does alow rendering.
Where am I wrong? What is a viable solution?
You are mixing up the mailto inside a string with those email addresses.
Try this:
<td><a :href="'mailto:' + user.email | lowercase">{{user.email | lowercase}}</a></td>
var app = new Vue({
el: '#app',
data () {
return {
users: null,
loading: true,
errored: false
}
},
mounted () {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
this.users = response.data
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
}
});
Vue.filter('lowercase', function (value) {
return value.toLowerCase();
})
<script src="https://unpkg.com/axios#0.18.0/dist/axios.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="container">
<div class="panel panel-default table-container">
<div class="panel-heading">Users</div>
<div class="panel-body">
<table class="table table-striped table-bordered" id="dataTable">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>City</th>
</tr>
</thead>
<tbody>
<tr v-for="(user, index) in users">
<td>{{index + 1}}</td>
<td>{{user.name}}</td>
<td><a :href="'mailto:' + user.email | lowercase">{{user.email | lowercase}}</a></td>
<td>{{user.address.city}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
My guess is that the | lowercase pipe / filter requires one argument and you're attempting to add two strings together ('mailto:' + user.email) immediately before it.
Try replacing with either: :href="('mailto:' + user.email) | lowercase"
or making 'mailto:' + user.email a computed property
I'm new to vue.js and I couldn't find a way to render the following data into an html table with rowspans using vue.
{
"title":"Monthly Sales",
"monthlySales":[
{
"product":"P123",
"months":[
{
"month":"January",
"unitPrice":"$80",
"unitsSold":2200
},
{
"month":"February",
"unitPrice":"$82",
"unitsSold":1900
},
{
"month":"March",
"unitPrice":"$81",
"unitsSold":1800
}
]
},
{
"product":"Q456",
"months":[
{
"month":"January",
"unitPrice":"$20",
"unitsSold":200
},
{
"month":"February",
"unitPrice":"$22",
"unitsSold":100
}
]
}
]
}
I wanted to create an output like this: http://jsbin.com/hucufezayu/edit?html,output
How can we render this kind of table with this data?
This should do the trick:
<template>
<div id="app">
<table border="1" style="border-collapse: collapse">
<thead>
<th>Product</th>
<th>Month</th>
<th>Unit price</th>
<th>No. sold</th>
</thead>
<tbody>
<template v-for="mSale in salesData.monthlySales">
<tr v-for="(month, key) in mSale.months">
<td v-if="key == 0" :rowspan="mSale.months.length"> {{mSale.product}}</td>
<td>{{month.month}}</td>
<td>{{month.unitPrice}}</td>
<td>{{month.unitsSold}}</td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
salesData: jsonData
}
}
}
</script>
I am new to vue and I am getting the error referenceError: items is not defined. Can anyone see why this happens or give me some pointers?
I think it has something to do with the items not being set on first look at the template.
My code:
<div id="root">
<task-list></task-list>
<template id="my-parent">
<table>
<thead>
<tr>
<th>Name</th>
<th>id</th>
</tr>
</thead>
<tbody>
<tr is="task" v-for="item in items" :item="item"></tr>
</tbody>
</table>
</template>
<template id="my-child">
<tr>
<td></td>
<td>{{ item.name }}</td>
<td>{{ item.id }}</td>
</tr>
</template>
</div>
<script>
Vue.component('task-list', {
template: '#my-parent',
data: function() {
return {
items: []
}
},
methods: {
getMyData: function(val) {
var _this = this;
$.ajax({
url: 'vuejson.php',
method: 'GET',
success: function (data) {
console.log(data);
_this.items = data;
},
error: function (error) {
alert(JSON.stringify(error));
}
})
}
},
mounted: function () {
this.getMyData("0");
}
});
Vue.component('task', {
template: '#my-child',
props: ['item'],
data: function() {
return {
item: {}
}
}
});
new Vue({
el: "#root",
});
</script>
Here is working modified code:
<template id="my-child">
<tr>
<td>{{ item.name }}</td>
<td>{{ item.id }}</td>
</tr>
</template>
<template id="my-parent">
<table>
<thead>
<tr>
<th>Name</th>
<th>id</th>
</tr>
</thead>
<tbody>
<tr is="task" v-for="item in items" :item="item"></tr>
</tbody>
</table>
</template>
<div id="root">
<task-list></task-list>
</div>
And js:
Vue.component('task-list', {
template: '#my-parent',
data: function() {
return {
items: []
}
},
methods: {
getMyData: function(val) {
var _this = this;
_this.items = [{name: '1.name', id: 1}];
}
},
mounted: function () {
this.getMyData("0");
}
});
Vue.component('task', {
template: '#my-child',
props: ['item'],
data: function() {
return {
}
}
});
new Vue({
el: "#root",
});
jsfiddle
It would be much easier to you to work with vue, if you do some tutorials first:)
edit:
And one more thing: if you declare property(item in your case), dont use that name in data.
So, what I did:
- placed templates outside of your root element
- removed "item" from data
You should describe templates outside div#root.
Example
<div id="root">
<task-list></task-list>
</div>
<template id="my-parent">
<table>
<thead>
<tr>
<th>Name</th>
<th>id</th>
</tr>
</thead>
<tbody>
<tr is="task" v-for="item in items" :item="item"></tr>
</tbody>
</table>
</template>
<template id="my-child">
<tr>
<td></td>
<td>{{ item.name }}</td>
<td>{{ item.id }}</td>
</tr>
</template>
Because if they are in #root they are part of vue instance for it, and there is no items in it.
new Vue({
el: "#root",
//no items
});
I just began learning AngularJS, and I am trying to create a pretty simple web application. Right now, I have some users. Each user is displayed in an individual table and each user has its own details which is displayed in a table under it.
$scope.userList = [{username: "Bob_t", detailsId: 1}, {username: "Mike_V", detailsId: 2}];
$scope.userDetails = [{Name: "Bob", Age: 20, id: "1"}, {Name: "Michael", Age: 18, id: "2"}];
You can see that each user has a reference to it's corresponding details (detailsId in userList corresponds to id in userDetails).
Basically, what I'm trying to do is initially have the details table hidden for each user. And when someone clicks the expand button for a specific user, open that user's corresponding details table and populate it with that user's details. What I'm having trouble with is getting the detailsId from the clicked expand button and then using that to query my DB to get the correct user's details to display in the table under it.
<div ng-repeat="user in userList | filter:searchBox">
<div class="uk-panel-box-secondary uk-overflow-container tableDiv uk-margin-large-bottom">
<table class="uk-table uk-table-hover uk-margin-top">
<thead>
<tr>
<th>Username</th>
</tr>
</thead>
</table>
<a class="uk-margin-bottom uk-margin-left" id="expandIcon" ng-click="isOpened=!isOpened; showOrHideDetails(isOpened, param)" ng-class="{'uk-icon-plus-square-o': !isOpened, 'uk-icon-minus-square-o': isOpened}"></a>
</div>
<div class="uk-panel-box-secondary uk-overflow-container uk-margin-top uk-margin-large-left uk-margin-bottom tableDiv">
<table class="uk-table uk-table-hover uk-margin-top">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th></th>
</tr>
</thead>
<tr ng-repeat="details in userDetails" id={{user.id}}>
<td>{{details.Name}}</td>
<td>{{details.Age}}</td>
<td>
</td>
</tr>
</table>
</div>
</div>
my controller:
$http({
method : "GET",
url : "http://database:8081/userAccounts/"
}).then(function mySucces(response) {
$scope.userList = response.data;
}, function myError(response) {
// $scope.userList = response.statusText;
});
$scope.showOrHideDetails = function(isOpened, param)
if(isOpened){
console.log($scope.id)
$scope[id] = true;
console.log($scope.id);
$http({
method : "GET",
url : "http://database:8081/details?id=" + index
}).then(function mySucces(response) {
$scope.userDetails = response.data;
}, function myError(response) {
$scope.userDetails = response.statusText;
});
}
else{
$scope.showDetails = false;
}
}
What is really confusing to me is once I get the correct userDetails object after querying the DB, how do I populate the corresponding table with that info?
I know I probably need a model, but this confuses me because the number of users is unknown.
First, your code is a bit confuse..
After each query you're attributing the response (which one is the details of a single user) to the whole array userDetails:
$scope.userDetails = response.data;
While it should be:
$scope.userDetails.push(response.data);
In addition, you have a single variable called isOpened, for sure it won't work, because you have multiple buttons for only 1 variable.
So my suggestion is to change it to:
<a class="uk-margin-bottom uk-margin-left" id="expandIcon" ng-click="showOrHideDetails(user)" ng-class="{'uk-icon-plus-square-o': !user.isOpened, 'uk-icon-minus-square-o': user.isOpened}"></a>
Also you have to check if the userDetail is already in your userDetails array.
Finally, since you want to show the details based on the user, you can use the native filter, because you already have the id property of users in both arrays, as below:
<tr ng-if="user.isOpened" ng-repeat="details in userDetails | filter: { id: user.detailsId }" id={{user.id}}>
A simple demo:
(function() {
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope', '$http'];
function MainCtrl($scope, $http) {
$scope.userList = [{
username: "Bob_t",
detailsId: 1
}, {
username: "Mike_V",
detailsId: 2
}];
$scope.userDetails = [{
Name: "Bob",
Age: 20,
id: "1"
}, {
Name: "Michael",
Age: 18,
id: "2"
}];
$scope.showOrHideDetails = function(user) {
user.isOpened = !user.isOpened;
function mySuccess(response) {
$scope.userDetails.push(response.data);
}
function myError(response) {
console.log(response.statusText);
}
if (user.isOpened) {
$http.get('http://database:8081/details?id=' + user.id)
.then(mySuccess)
.catch(myError);
} else {
$scope.showDetails = false;
}
}
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.26.4/js/uikit.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.26.4/css/uikit.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="user in userList | filter:searchBox">
<div class="uk-panel-box-secondary uk-overflow-container tableDiv uk-margin-large-bottom">
<table class="uk-table uk-table-hover uk-margin-top">
<thead>
<tr>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-bind="user.username"></td>
</tr>
</tbody>
</table>
<a class="uk-margin-bottom uk-margin-left" id="expandIcon" ng-click="showOrHideDetails(user)" ng-class="{'uk-icon-plus-square-o': !user.isOpened, 'uk-icon-minus-square-o': user.isOpened}"></a>
</div>
<div class="uk-panel-box-secondary uk-overflow-container uk-margin-top uk-margin-large-left uk-margin-bottom tableDiv">
<table class="uk-table uk-table-hover uk-margin-top">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th></th>
</tr>
</thead>
<tr ng-if="user.isOpened" ng-repeat="details in userDetails | filter: { id: user.detailsId }" id={{user.id}}>
<td ng-bind="details.Name"></td>
<td ng-bind="details.Age"></td>
<td>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>
I hope it helps!!
have you tried passing user from
<div ng-repeat="user in userList | filter:searchBox">
to function showOrHideDetails(user)
I'm trying to implement knockoutjs and requirejs to my asp.net mvc app.
So, here's what I have.
Views/Shared/_Layout.cshtml
<html>
<body>
#RenderBody()
<script src="~/Scripts/require.js" data-main="/Scripts/app/main"></script>
#RenderSection("scripts", required: false)
</body>
<html>
Scripts/main.js
require.config({
baseUrl: '/Scripts',
paths: {
ko: '/Scripts/knockout-3.3.0'
}
});
Views/Product/Index.cshtml (one of my views)
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: products">
<tr>
<td data-bind="text: $data.product"></td>
</tr>
</tbody>
</table>
<script src="~/Scripts/app/product.js"></script>
#section scripts {
// Some scripts here
}
Scripts/app/product.js
define(['ko'], function (ko) {
var data = [
{ name: 'Product1' },
{ name: 'Product2' }
];
var Product = function () {
this.name = ko.observable()
};
var productVm = {
products: ko.observableArray([]),
load: function() {
for (var i = 0; i < data.length; i++) {
productVm.products.push(new Product()
.name(data[i].name));
}
}
}
productVm.load();
ko.applyBindings(productVm);
});
Just in case you need to see my folder structure
Solution
- Scripts
-- app
--- product.js
-- require.js
-- knockout-3.3.0.js
- Views
-- Product
--- Index.cshtml
-- Shared
--- _Layout.cshtml
Then, once I navigate to my products index page. I got a define is not define error. What am I missing?
Include following in your main.js
require(["app/product"], function () {
});
And modify index.html as follows
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: products">
<tr>
<td data-bind="text: $data.name"></td>
</tr>
</tbody>
</table>
#section scripts {
}
If you intend to use RequireJS for multi page application, then also read this.