Custom list rendering in Meteor - meteor

I need to display items as couples. Like this for example:
Template.container.couples = function() {
var items = Items.find({}, {sort: {sort_field: 1}}).fetch();
var couples = [];
for (var i = 0; i < items.length; i++) {
couples.push({
itemA: items[i],
itemB: items[i + 1]
});
i++;
}
return couples;
};
<template name="container">
<ul>
{{#each couples}}
<li>
<p class="item-a">{{>item itemA}}</p>
<span>|</span>
<p class="item-b">{{>item itemB}}</p>
</li>
{{/each}}
<ul>
</template>
<template name="item">
<strong>{{title}}</strong>
</template>
Items look like:
{
sort_field: 1,
title: 'Item 1',
type: 'A'
},
{
sort_field: 2,
title: 'Item 2',
type: 'B'
},
{
sort_field: 3,
title: 'Item 3',
type: 'A'
},
{
sort_field: 4,
title: 'Item 4',
type: 'B'
},
{
sort_field: 5,
title: 'Item 5',
type: 'A'
}
This code works good, but when I update title for one of items then all items rerender.
How to fix it? How to create this kind of layout with reactivity?

As of Meteor 0.8.0, Meteor will automagically only re-render content that changes.

Related

Vue 3 composition API object data not updating after apply the filter

I have one reactive object of the product list, I am showing the products in my template. Now I made one filter which filters the product object by
the product type key of the object. The issue is when I applied the filter it shows values proper in the console but in the template, it doesn't reflect any changes. In vue 2 I used this.$forceUpdate(); and it seems to work for me. What is the correct way to do this thing in vue3 composition API?
I am doing something wrong but didn't get the exact idea of what to do for this. Thank you in advacne.
Here I attached my code sandbox link
Code sandbox
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button #click="filterProductData()">FILTER</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let filteredProducts = reactive({});
const filterProductData = () => {
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
filteredProducts[key] = productInitData[key];
}
});
productData = filteredProducts;
console.log(productData);
};
return {
productData,
productTypeVal,
filterProductData,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Please have a look at the Vue.js Documentation: Limitations of reactive():
The reactive() API has two limitations:
It only works for object types (objects, arrays, and collection types such as Map and Set). It cannot hold primitive types such as string, number or boolean.
Since Vue's reactivity tracking works over property access, we must always keep the same reference to the reactive object. This means we can't easily "replace" a reactive object because the reactivity connection to the first reference is lost.
The latter point is the issue with your code: you're not changing the state of this object's properties, which is allowed, but rather changing the reference that the variable refers to and I think that this reassignment of reference is what is causing the loss of reactivity.
So there are several possible solutions that I see you can use, including:
Changing the actual object properties of productData. To do this, you probably will want to remove all the properties and re-add them from productInitData if they meet criteria:
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
Using and displaying a computed property where the v-for is on the computedProducts field. This may require other fields, and in my example, I use a boolean field, isFiltered:
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
and in the template:
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button #click="toggleFiltered()">FILTER 1</button>
Wrapping your reactive object in another object so that the inner object's reference can be changed and it remains reactive. But if we're going this far, might as well use a store such as Pinia or Vuex.
For example:
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button #click="toggleFiltered()">FILTER 1</button>
<br />
<br />
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button #click="myFilter">FILTER 2</button>
</div>
</template>
<script>
import { computed, reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
return {
productData,
productTypeVal,
computedProducts,
toggleFiltered,
myFilter,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

I want to send data from product array to cart array and it shows values undefined

I want to send data from product array to cart array and it shows values undefined
that's my vue script i want to add to cart the selected product proid and price
this card is also an array and i want to push those two values into object
<script>
export default {
name: "ClothesView",
props: {
carts: {
type: Array,
required: true,
},
idi: String,
prico: Number,
},
data: function () {
return {
cartos: this.carts,
products: [
{
id: 1,
img: require("#/assets/clothes/clo.png"),
proid: "Frttt14",
price: 10,
},
{
id: 2,
img: require("#/assets/clothes/clo2.png"),
proid: "vc4555rt141",
price: 8,
},
{
id: 3,
img: require("#/assets/clothes/clo10.png"),
proid: "sd5rt141",
price: 120,
},
{
id: 6,
img: require("#/assets/clothes/clo12.png"),
proid: "kojkrt141",
price: 14,
},
{
id: 7,
img: require("#/assets/clothes/clo13.png"),
proid: "nmkt141",
price: 100,
},
{
id: 8,
img: require("#/assets/clothes/clo15.png"),
proid: "nghgj778",
price: 41,
},
{
id: 9,
img: require("#/assets/clothes/clo16.png"),
proid: "87878kll",
price: 56,
},
],
};
},
methods: {
addtocart() {
this.cartos.push({
proid: this.products.proid,
price:this.products.price
});
console.log(this.cartos);
},
},
};
</script>
i couldn't get the data inside product item to push it into the array
this.products is Array, so this.products.proid/price is undefined.
maybe you want to do this (forEach is OK too)
for(let i = 0; i < this.products.length; i++) {
this.cartos.push({
proid: this.products[i].proid,
price: this.products[i].price
})
}
// in your case, if you want to add the selected product to cart
// you should v-for your products first
```
<template>
<div v-for="item in products" :key="item.id" #click="addtocart(item)">
{{ item.proid }} - {{ item.price }}
</div>
</template>
<script>
methods: {
addtocart(item) {
this.cartos.push({
proid: item.proid,
price: item.price
});
console.log(this.cartos);
}
}
</script>
```

IonicPopup i cant take data from ng-model

I am new at this correct me if I'm wrong . I want to take the name data from the input with ng-model but i cant take the data at the onTap: function. I tried to alert $scope.name but it is empty doesnt work,then i tried $scope.name="" empty at the init but it doesnt change when the user writes his name.How can i get the name .Thank you.
`$scope.data = {
model: "Choose",
availableOptions: [
{id: '1', name: 'dummyText1'},
{id: '2', name: 'dummyText2'},
{id: '3', name: 'dummyText3'}
]
};`
var myPopup = $ionicPopup.show({
template:
'<div class="list list-inset">' +
'</label><label class="item item-input"><i class="icon ion-person placeholder-icon"></i>' +
'<input type="text" placeholder="Your name.." ng-model="name"></label>' +
'</div>' ,
title: 'Profile',
scope: $scope,
buttons: [
{ text: 'Cancel' },
{
text: '<b>Save</b>',
type: 'button-positive',
onTap: function(e) {
if ($scope.name != null) {
alert($scope.name);
e.preventDefault();
} else {
alert($scope.name);
}
}
}
]
});
here is the popup screenshot
Thank you again
I figured out with adding ng-change="foo(data)" to input ,now im able to use this data at scope .
Thank you.

Using package aslagle:reactive-table with X-editable

I'm using reactive table package from aslagle in my app and I want to create in-line editing, I searched and I found that there's x-editable package for Meteor, so how can I use aslagle:reactive-table package with workman:x-editable-reactive-template package?
I tried this:
Reactive-Table settings:
tableSettings: function () {
return {
collection: fLogCollection,
rowsPerPage: 10,
showFilter: true,
fields: [
{ key: 'name', label: 'Name'},
{ key: 'amount',
label: 'Amount',
tmpl: Template.xEditableAmount
},
{ key: 'cashFrom', label: 'Cash From'},
{ key: 'dateIs', label: 'Date', sortOrder: 0, sortDirection: 'descending'},
{ key: 'controls', label: 'Controls', fn: function () {
return new Spacebars.SafeString(
"<button class='editFlog'><span class='glyphicon glyphicon-pencil'></span> </button>"+
"<button class='delete'><span class='glyphicon glyphicon-remove'></span> </button>"
); } },
{ key: 'createdAt', label: 'createdAt', hidden: true },
],
};
},
xEditableAmount template:
<template name="xEditableAmount">
{{amount}}
</template>
This code to get the x-editable rendered:
Template.fLog.onRendered(function() {
this.$('.editable').editable({
success: function (response, newValue) {
if(response.status == 'error') return response.msg; //msg will be shown in editable form
else Meteor.call('flog.edit2', this._id, newValue);
},
});
});
I succeeded in making x-editable render but
I failed at getting the field updated with the new value in collection...
You can inject templates into fields which makes it convenient to add almost anything you want.
Template helper:
tableSettings: function() {
return {
collection: foo,
fields: [
{
key: 'foo_1',
label: 'Foo 1',
tmpl: Template.foo1,
},
{
key: 'foo_2',
label: 'Foo 2',
tmpl: Template.foo2,
},
{
key: 'foo_2',
label: 'Foo 2',
tmpl: Template.foo2,
}
]
};
}
In foo2 helper (copied directly from workman/x-editable-reactive-template atmosphere page):
Template.foo2.helpers({
onSuccess: function () {
var id = this._id;
return function (res, val) {
MyColl.update({ _id: id }, { $set: { prop: val } });
}
}
});
In your Templates:
<template name='table>
{{> reactiveTable settings=tableSettings}}`
</template>
<template name='foo1'>
<!-- Any html (below pasted from docs (link at bottom of post)-->
superuser
</template>
<template name='foo2'>
{{> xEditable type="text" success=onSuccess placement="right" }} <!-- put your workman:x-editable-reactive-template here -->
</template>
This should get you pointed in the right direction.
https://vitalets.github.io/x-editable/docs.html

Ember-data loading async and order results according with property

I'm learning about ember/ember-data and would like to fetch some data from server and order it.
My json data is something like
{
'ninjas': [
{ id: '1', name: 'Ninja 1', age: 23},
{ id: '2', name: 'Ninja 2', age: 27},
{ id: '3', name: 'Ninja 3', age: 22}
],
'clans': [
{ id: '1566', title: 'Foot Clan', ninja_ids: ['1', '2']},
{ id: '8941', title: 'Hand Clan', ninja_ids: ['3']}
]
}
The templates are
<script type="text/x-handlebars" data-template-name="clans">
<div>Clans</div>
<ul>
{{#each controller}}
<li>
{{#link-to 'clan' this}}{{title}}{{/link-to}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="clan">
<div>{{title}}</div>
{{#each fetchedNinjas}}
<ul>
<li>{{fetchedNinjas}}</li>
</ul>
{{/each}}
</script>
Here is the basic App script:
var App = Ember.Application.create();
App.AplicationStore = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({})
});
App.Router.map(function() {
this.resource('ninjas');
this.resource('clans');
this.resource('clan', {path: 'clans/:clan_id'});
});
The ninja script is here:
App.Ninja = DS.Model.extend({
name: DS.attr('string'),
age: DS.attr('number'),
clans: DS.hasMany('clan')
});
App.NinjasRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('ninja');
}
});
Here is the Clan Model, ClansRoute, ClanRoute
App.Clan = DS.Model.extend({
title: DS.attr('string'),
ninjas: DS.hasMany('ninja', {async: true})
});
App.ClansRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('clan');
}
});
App.ClanRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('clan', params.clan_id);
}
});
I think that I should get the related ninja data on ClanController and then order it, but I don't know how to proceed.
App.ClanController = Ember.ObjectController.extend({
fetchedNinjas: function () {
//should I use a promise here?
}.property('ninjas')
});

Resources