Is it possible to set multiple decorators for a single node? - ractivejs

Something like
<button decorator='initbutton:p1, p2; tooltip:Text' />
I have found a decorator helper plugin for combining decorators:
http://martypdx.github.io/ractive-decorators-helpers/
Is it the best option right now?

Use ractive-multi-decorator.
First install it:
npm install ractive-multi-decorator --save
Then add it to ractive:
var ractive = new Ractive({
...
decorators: {
multi: require('ractive-multi-decorator')
},
...
});
Then use it:
<div
id="myelement"
decorator="multi:{
localdecorator: [ {{ dynamicArg1 }}, arg2, arg3 ],
globaldecorator: {{singleArg}},
anotherdecorator: true
}"
></div>
Credits go to akhouzy and martypdx.

Ractive doesn't natively support multiple decorators, at least not yet. The decorator helper plugin that you found is the best option right now.
Someone opened an issue on GitHub to request this feature if you want to chime in.

Related

how to attach multiple events to a ractive element

The documentation describing method call event handling suggests that it's possible to attach multiple handlers to an event separated by a comma, like this:
<p on-click='speak(), bark()'>{{name}}</p>
However this throws an error when the element is first rendered, so clearly I'm misunderstanding the docs. Can someone please help me to understand how to attach multiple handlers to the on-click event.
thanks in advance
Les
I believe you're still using 0.7 or older. Update to the latest version (0.8.7 as of this writing) and you'll be able to do the following:
const Component = Ractive.extend({
template: `
<button on-click="#this.foo(), #this.bar()">Click Me</button>
`,
foo(){
alert('Hello')
},
bar(){
alert('World!')
}
});
new Component({ el: 'body' });

Meteor Package - Iron:router - How to prevent my package routes to execute the hooks of the apps that use it?

I am creating a meteor package using iron:router, it works great.
The routes logic of this package is very specific.
But as soon as I add this package in a Meteor app that is also using iron:router, the hooks defined by the Meteor app (onBeforeAction, onAfterAction, ...) are called for the routes created by the package.
I'd like to prevent my package routes to execute the hooks of the app.
Is there a simple piece of code I could use to isolate the routes of my package from the "external" hooks? Maybe I could create a specific RouteController?
The last option for me is to implement a router from scratch...
Thank you!
The code that solved my issue is the following:
let MyRouter = new Iron.Router;
MyRouter.onBeforeAction(Iron.Router.bodyParser.json());
MyRouter.onBeforeAction(Iron.Router.bodyParser.urlencoded({extended: false}));
The solution is to declare a new isolated router. The 2 lines under the router declaration enable to retrieve the data of POST requests.
Yes, you need to create isolated controller:
var MyPackageController = RouteController.extend({
onBeforeAction: function () { ... },
onAfterAction: function () { ... }
/* other options */
});
Router.map(function () {
this.route('packageRoute', {
controller: MyPackageController
/* other options */
});
});

How to Two-way Data Binding Between Parents and grandchildren in Vue.js

I faced a problem, I solve it by cookies but I want to solve the problem without cookies. I have a component which called app-header and It has another component which called outmodal.
Now, My first Vue instance require component app-header.
var vue = new Vue({
el : "html",
data : {
title : "Site Title",
description : "description of page",
keywords : "my keywords",
view : "home",
login : "login"
},
components:{
"app-header" :require("../../components/header"),
"app-footer" :require("../../components/footer"),
"home" :require("../../views/home")
},
});
code of app-header
var Vue = require("vue");
Vue.partial("login",require("../../partials/login.html"));
Vue.partial("logged",require("../../partials/logged.html"));
module.exports = {
template : require("./template.html"),
replace : true,
components : {
outmodal : require("../outmodal")
},
props : ['login']
}
code of outmodal
var Vue = require("vue");
Vue.partial("loginModal",require("../../partials/loginModal.html"));
module.exports = {
template : require("./template.html"),
replace : true,
props : ['name'],
data : function () {
return {
userLogin : { mail : "", password : "", remember : ""}
}
},
methods : {
formSubmit : function(e){
e.preventDefault();
this.$http.post("http://example.com/auth/login",{ "email": this.userLogin.mail , "password": this.userLogin.password },function(data,status,request){
$.cookie("site_token",data.token,{expires : 1})
}).error(function(data,status,request){
});
}
}, ready : function(){
console.log("it works")
}
}
In outmodal component I connect the API and I check the login, If login will be succesfull, I want to change value of login variable in my Vue instance. I use web pack to build all requires. So I don't know how can I data binding between these files.
How can I solve It? I
The Best Solution which I found
For 0.12
http://012.vuejs.org/guide/components.html#Inheriting_Parent_Scope
for 1.0
http://v1.vuejs.org/guide/components.html#Parent-Child-Communication
for 2.0
https://v2.vuejs.org/v2/guide/components.html#Composing-Components (use props to one-way bind data from parent to child)
There are several ways of doing it, and some are mentioned in other answers:
Use props on components
Use v-model attribute
Use the sync modifier (for Vue 2.0)
Use v-model arguments (for Vue 3.0)
Use Pinia
Here are some details to the methods that are available:
1.) Use props on components
Props should ideally only be used to pass data down into a component and events should pass data back up. This is the way the system was intended. (Use either v-model or sync modifier as "shorthands")
Props and events are easy to use and are the ideal way to solve most common problems.
Using props for two-way binding is not usually advised but possible, by passing an object or array you can change a property of that object and it will be observed in both child and parent without Vue printing a warning in the console.
Because of how Vue observes changes all properties need to be available on an object or they will not be reactive.
If any properties are added after Vue has finished making them observable 'set' will have to be used.
//Normal usage
Vue.set(aVariable, 'aNewProp', 42);
//This is how to use it in Nuxt
this.$set(this.historyEntry, 'date', new Date());
The object will be reactive for both component and the parent:
I you pass an object/array as a prop, it's two-way syncing automatically - change data in the
child, it is changed in the parent.
If you pass simple values (strings, numbers)
via props, you have to explicitly use the .sync modifier
As quoted from --> https://stackoverflow.com/a/35723888/1087372
2.) Use v-model attribute
The v-model attribute is syntactic sugar that enables easy two-way binding between parent and child. It does the same thing as the sync modifier does only it uses a specific prop and a specific event for the binding
This:
<input v-model="searchText">
is the same as this:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
Where the prop must be value and the event must be input
3.) Use the sync modifier (for Vue 2.0)
The sync modifier is also syntactic sugar and does the same as v-model, just that the prop and event names are set by whatever is being used.
In the parent it can be used as follows:
<text-document v-bind:title.sync="doc.title"></text-document>
From the child an event can be emitted to notify the parent of any changes:
this.$emit('update:title', newTitle)
4.) Use v-model arguments (for Vue 3.0)
In Vue 3.x the sync modifier was removed.
Instead you can use v-model arguments which solve the same problem
<ChildComponent v-model:title="pageTitle" />
<!-- would be shorthand for: -->
<ChildComponent :title="pageTitle" #update:title="pageTitle = $event" />
5.) Use Pinia (or Vuex)
As of now Pinia is the official recommended state manager/data store
Pinia is a store library for Vue, it allows you to share a state across components/pages.
By using the Pinia store it is easier to see the flow of data mutations and they are explicitly defined. By using the vue developer tools it is easy to debug and rollback changes that were made.
This approach needs a bit more boilerplate, but if used throughout a project it becomes a much cleaner way to define how changes are made and from where.
Take a look at their getting started section
**In case of legacy projects** :
If your project already uses Vuex, you can keep on using it.
Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead.
i found this one to be more accurate.
https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
only in 2.3.0+ tho.
and honestly it's still not good enough. should simply be a easy option for 'two-way' data binding. so none of these options is good.
try using vuex instead. they have more options for such purpose.
https://vuex.vuejs.org/en/state.html
I would prefer event-driven updates as recommended in the documentation. However, I was limited by the existing ("third-party") component already using props and $emit. This component is my grandchild. The following is my solution (passing value through child using props, sync and computed value with $emit.
Comments are welcome.
Value can be modified in parent and grandchild without error:
Grandchild (simplified third-party component):
<template>
<div v-show="value">{{ value}}</div>
<button #click="closeBox">Close</button>
</template>
<script>
export default {
props: {
value: null
},
methods: {
closeBox() {
this.$emit('update:value', null);
}
}
}
</script>
Child:
<template>
<grandchild-component :value.sync="passedValue" />
</template>
<script>
export default {
props: {
value: null
},
computed: {
passedValue: {
get() {
return this.value;
},
set(newVal) {
this.$emit('update:value', newVal);
}
}
}
}
</script>
Parent:
<template>
<child-component :value.sync="value" />
</template>
<script>
export default {
data() {
return {
value: null,
}
},
// ... e.g. method setting/modifying the value
}
</script>

Handlebars + Meteor + iron-router

I am using iron-router for my meteor project and everything was going fine but I just ran into some strange behavior.
I have a loop set up for a list of items that looks something like this.
{{#each list_items}}
<div>{{user.username}}
Click here!
</div>
{{/each}}
The JSON object for my user looks something like this:
{
user: {
username: jdoe
},
images: {
low-res-url: http://example.com
},
link: http://example.com/profile
}
Now the {{user.username}} shows up as expected but when I try to put the {{link}} in the href I get an error from iron-router saying
"You called Router.path for a route named undefined but that that route doesn't seem to exist. Are you sure you created it?"
Any help or advice would be appreciated.
Under the hood Iron-Router registers handelbars helper:
Handlebars.registerHelper('link', function (options) {
...
});
Simply change field link to different name like my_link.
As #perhelium mentioned Iron-Router has specified a helper named 'link'
Handlebars.registerHelper('link', function (options) {...});
In order to access an item named 'link' in your JSON object you need to explicitly refer to the JSON object itself.
So your line: Click here!
Would need to be specified as Click here!

How to debug template in Meteor/handlebars?

According to this blog post, I should register a helper to better debug handlebars templates, but is not working:
ReferenceError: Handlebars is not defined
So, how can I {{debug}} in Meteor/handlebars?
This is the helper function I use for debugging in my own projects:
Template.registerHelper("debug", function(optionalValue) {
console.log("Current Context");
console.log("====================");
console.log(this);
if (optionalValue) {
console.log("Value");
console.log("====================");
console.log(optionalValue);
}
});
You can then call it in your templates with {{debug}} and it displays the context you are currently in. Read more at http://docs.meteor.com/#/full/template_registerhelper.
In Meteor 0.4.0 you register handlers like this:
Template.myTemplate.helpers({
helper: function () {
// some code here
console.log(arguments);
}
});
There is no need to call Handlebars directly.
Make sure that you register your helper in client (or shared) meteor code.
Handlebars.registerHelper('helper', function() {
// Do stuff
});
This should be callable via {{helper}} in your templates.
For the sake of completeness: you can also use
Template.registerHelper('helper', helperFunc);
instead of Handlebars.regsterHelper('h',f);
A small reason this is better is that then your app won't need that much refactoring if you decide somewhere down the road that you want to use something else instead of Handlebars(i.e. Spacebars, the real name of meteors adaption) like jade for meteor.
This is really a comment to the accepted answer. Looking forward to one day hit 50 rep.

Resources