In jinja2(python) template engine there is a simple thing for truncating strings:
{{ fooText|truncate(200) }}
Does meteor(handlebars) provides something like this?
I am using values as options, starting value as well as ending value passed as arguments form template. Try this:
Handlebars.registerHelper('trimString', function(passedString, startstring, endstring) {
var theString = passedString.substring( startstring, endstring );
return new Handlebars.SafeString(theString)
});
In template:
<p>{{{trimString value 0 300}}}</p>
it'll print first 300 characters of the value. Hope this help you.
I never use | on spacebars (the engine used on meteor template), but you can do a helper to accomplish this(for example a global Template.registerHelperr).
Template.registerHelper('text', function(passedString) {
var fooText = passedString.substring(0,1); //same as truncate.
return new Spacebars.SafeString(fooText)
});
And use it like {{ text myString}}
Here we are using some Blaze and the substring method.
Related
I have a string of sequence say "aby,abraham,issac,rebecca,job,david,daniel" now I need to add space after the comma.
I bind the value using ng-bind and display the result using ng-show. I'm unable to use join as it is received as array from the database.
You can directly bind like this,
<span ng-bind="users.name.split(',').join(', ')" ng-show="users.name"></span>
No need to create filters.
To initialize the value, use ng-init directive to hardcode the value to Username
ng-init="Username='aby,abraham,issac,rebecca,job,david,daniel'"
You can use filter to do this task
.filter('commaSpace', [function () {
return function (str) {
return str.replace(',/g', ', ');
};
}])
I tried the following code to pass data to a template and receive it in onCreated() but I cannot access the data.
deviceInfo.js:
BlazeLayout.render('layout',{main:'deviceInfo',stats:'paramstats',attr:"SOME_DATA"});
deviceInfo.html:
{{>Template.dynamic template=stats data=attr}}
paramstats.js:
Template.paramstats.onCreated( () => {
console.log("onCreated");
console.log("Data is:",this.data.attr);
});
But I get TypeError: Cannot read property 'attr' of undefined.
where am I going wrong?
You need to use the normal function syntax for onCreated callback. Arrow function will bind the context of your function to the outer scope automatically, it is the cause of your problem. Try this:
Template.paramstats.onCreated(function() {
console.log("onCreated");
console.log("Data is:",this.data.attr);
});
I am using Meteor 1.4.# and I was able to retrieve the parameters like so:
BlazeLayout.render("page", {
params: ['fullscreen', 'route']
});
// page.js
Template.page.onCreated(function() {
let params = this.data.params();
console.log(params);
}
Not quite sure why you're using two levels of indirection. BlazeLayout.render() is giving you one level and then you're using a dynamic template within that? Why not directly render the template you ultimately want using BlazeLayout.render()?
In any case, you're dereferencing your data context indirectly.
In the BlazeLayout.render() call you're setting the attr variable to some value.
Then in your dynamic template you're using data=attr but this means that inside your template helpers that this is going be have the value of attr. There will be no data subkey added automatically.
You don't show the value that you're setting for attr so it's not even clear that you have an attr subkey in your attr variable, that would also be confusing to anyone else who ever tries to debug your code.
#khang is correct about not using the arrow function syntax in onCreated(), try:
Template.paramstats.onCreated(function(){
console.log("onCreated");
console.log("Data is:",this);
});
this should have whatever value you stuffed into attr in your BlazeLayout.render()
I thought it'd be easy but, yeah... it wasn't. I already posted a question that went in the same direction, but formulated another question.
What I want to do
I have the collection songs, that has a time attribute (the playing-time of the song). This attribute should be handled different in the form-validation and the backend-validation!
! I'd like to do it with what autoform (and simple-schema / collection2) offers me. If that's possible...
in the form the time should be entered and validated as a string that fits the regex /^\d{1,2}:?[0-5][0-9]$/ (so either format "mm:ss" or mmss).
in the database it should be stored as a Number
What I tried to do
1. The "formToDoc-way"
This is my javascript
// schema for collection
var schema = {
time: {
label: "Time (MM:SS)",
type: Number // !!!
},
// ...
};
SongsSchema = new SimpleSchema(schema);
Songs.attachSchema(SongsSchema);
// schema for form validation
schema.time.type = String // changing from Number to String!
schema.time.regEx = /^\d{1,2}:?[0-5][0-9]$/;
SongsSchemaForm = new SimpleSchema(schema);
And this is my template:
{{>quickForm
id="..."
type="insert"
collection="Songs"
schema="SongsSchemaForm"
}}
My desired workflow would be:
time is validated as a String using the schema
time is being converted to seconds (Number)
time is validated as a Number in the backend
song is stored
And the way back.
I first tried to use the hook formToDoc and converted the string into seconds (Number).
The Problem:
I found out, that the form validation via the given schema (for the form) takes place AFTER the conversion in `formToDoc, so it is a Number already and validation as a String fails.
That is why I looked for another hook that fires after the form is validated. That's why I tried...
2. The "before.insert-way"
I used the hook before.insert and the way to the database worked!
AutoForm.hooks({
formCreateSong: {
before: {
insert: function (doc) {
// converting the doc.time to Number (seconds)
// ...
return doc;
}
},
docToForm: function (doc) {
// convert the doc.time (Number) back to a string (MM:SS)
// ...
return doc;
}
}
});
The Problem:
When I implemented an update-form, the docToForm was not called so in the update-form was the numerical value (in seconds).
Questions:
How can I do the way back from the database to the form, so the conversion from seconds to a string MM:SS?
Is there a better way how to cope with this usecase (different data types in the form-validation and backend-validation)?
I am looking for a "meteor autoform" way of solving this.
Thank you alot for reading and hopefully a good answer ;-)
I feel like the time should really be formatted inside the view and not inside the model. So here's the Schema for time I'd use:
...
function convertTimeToSeconds (timeString) {
var timeSplit = timeString.split(':')
return (parseInt(timeSplit[0]) * 60 + parseInt(timeSplit[1]))
}
time: {
type: Number,
autoValue: function () {
if(!/^\d{1,2}:?[0-5][0-9]$/.test(this.value)) return false
return convertTimeToSeconds(this.value)
}
}
...
This has a small disadvantage of course. You can't use the quickForm-helper anymore, but will have to use autoForm.
To then display the value I'd simply find the songs and then write a helper:
Template.registerHelper('formateTime', function (seconds) {
var secondsMod = seconds % 60
return [(seconds - secondsMod) / 60, secondsMod].join(':')
})
In your template:
{{ formatTime time }}
The easy answer is don't validate the string, validate the number that the string is converted into.
With simpleschema, all you do is create a custom validation. That custom validation is going to grab the string, turn it into a number, and then validate that number.
Then, when you pull it from the database, you'll have to take that number & convert it into a string. Now, simpleschema doesn't do this natively, but it's easy enough to do in your form.
Now, if you wanted to get fancy, here's what I'd recommend:
Add new schema fields:
SimpleSchema.extendOptions({
userValue: Match.Optional(Function),
dbValue: Match.Optional(Function),
});
Then, add a function to your time field (stored as Date field):
userValue: function () {
return moment(this.value).format('mm:ss');
},
dbValue: function () {
return timeToNumber(this.value);
}
Then, make a function that converts a timeString to a number (quick and dirty example, you'll have to add error checking):
function timeToNumber(str) {
str.replace(':',''); //remove colon
var mins = +str.substr(0,2);
var secs = +str.substr(2,2);
return mins * 60 + secs;
}
Then, for real-time validation you can use schema.namedContext().validateOne. To update the db, just send timeToNumber(input.value).
In Meteor, we can pass values to a helper like so:
{{testHelper abc="123" xyz="987"}}
and the helper:
Template.registerHelper('testHelper', function(opt) {
console.log(opt.hash.abc);
console.log(opt.hash.xyz);
});
Is there a way to do just:
{{testHelper "123" "987"}}
and get the values back as an index instead? Like so:
Template.registerHelper('testHelper', function(opt) {
console.log(opt.hash[0]);
console.log(opt.hash[1]);
});
I know you can define a function which takes two arguments, like in this answer, and it'd work. But I'd ideally want the function to take any number of arguments.
If I console.log(opt); here, I'd get 123, whereas I want opt.hash[0] (or, even better, opt[0]) to be 123, and opt.hash[1] to be 987.
The reason I want this is
"123" looks cleaner than abc="123" when the key value doesn't matter,
but still allow the helper to accept any number of
parameters.
I have tried passing an array as a parameter {{testHelper ["123", "987"]}}, but that produced a syntax error.
This is a bit of an older question, but I just tested and something like this works:
// JS
Template.registerHelper('concat', function () {
return Array.prototype.slice.call(arguments, 0, -1).join('');
});
// Template
{{ concat "a" "b" "c" }}
Basically, the normal JavaScript arguments value is still available in a helper.
The last argument is always the Spacebars object (the one with the hash), but if you slice that off like I did (Array.prototype.slice.call(arguments, 0, -1)), you basically get all of the other arguments in an array, which you can then use as you like.
So this might not be a perfect solution but you can pass in an array if a helper for it already exists, so assuming you have a way to inject the values into the helper array, for instance with Session, you can access it in your original method.
Session.set('tempArray', ['abc', 'xyz']);
Template.registerHelper('tempArray', function() {
return Session.get('tempArray');
});
Template.registerHelper('readArray', function(arr) {
console.log(arr[0]);
});
<!-- template -->
{{readArray tempArray}}
//outputs: 'abc'
To get understandable links to share, I don't want to put only the ._id in the url but the .name as well.
Router.map(function () {
this.route('here', {
path: 'here/:_id/:name/',
template: 'here'
})
})
The Problem is that the .name entry can have special characters like /.
www.example.com/here/1234/name_with special-characters like / (<-this slash) /
Is there a way to replace the slash (and other special characters) in iron-router?
(if there is a good way to handle this, maybe in some cases I don't even need the id anymore.)
If I want to use <a href="{{pathFor 'showCourse'}}">
I can not use a wildecardpath: 'here/:_id/*
Thanks
It's not specific to Iron Router, but JavaScript's native global functions encodeURIComponent and decodeURIComponent exist for just this purpose:
encodeURIComponent("foo/bar"); // returns "foo%2Fbar"
decodeURIComponent("foo%2Fbar"); // returns "foo/bar"
What I do in my projects is add a field called slug and write a function that generates an URL-friendly slug from the document's title and checks the collection to make sure the slug is unique (otherwise it appends "-2" or "-3" etc. as appropriate). With a slug or similar field that is unique per document, you can use it as the only query parameter and forgo the _id.
Expanding on Geoffrey Booth's answer, you can do this with a template helper.
Define a template helper to encode your name value (I made it global so it can be reused by all templates):
Template.registerHelper('encodeName', function() {
this.name = encodeURIComponent(this.name);
return this;
});
Then, in your templates, you can pass this function to iron-router's pathFor helper:
<a href="{{pathFor 'showCourse' encodeName}}">
This works for me on Meteor 1.1.0.2.