I'm trying to make a simple custom aui:validator for the birthday field on the registration form but it is simply not working. Any ideas?
<aui:input name="birthday" value="<%=birthday%>">
<aui:validator name="custom" errorMessage="some-error">
function (val, fieldNode, ruleValue) {
return false;
}
</aui:validator>
</aui:input>
What I'd like to achieve is to check if the user is 18 years old at the registration.
Any hints would be greatly appreciated!
Here i am validating that selected date is greater than today's date.You can change as per your need.
<script>
AUI().ready('aui-form-validator', 'aui-overlay-context-panel', function(A) {
A.mix(
YUI.AUI.defaults.FormValidator.RULES,
{
dateCustomRule: function(val, fieldNode, ruleValue) {
var val = new Date(Date.parse(val,"MMM dd yyyy"));
var today=new Date();
return (val>today);
}
},
true
);
var validator2 = new A.FormValidator({
boundingBox: document.<portlet:namespace/>form,
fieldContainer: 'p',
rules: {
<portlet:namespace />dueDate:{
required: true,
dateCustomRule:true
}
},
fieldStrings: {
<portlet:namespace />dueDate: {
required: 'Due Date Required',
dateCustomRule: 'Date must be greater than Today\'s Date'
}
},
on: {
validateField: function(event) {
},
validField: function(event) {
},
errorField: function(event) {
},
submitError: function(event) {
var formEvent = event.validator.formEvent;
var errors = event.validator.errors;
},
submit: function(event) {
var formEvent = event.validator.formEvent;
return false;
}
}
});
});
</script>
Unfortunately I wasn't able to use Lucky Boy's solution either. For some mysterious reason it didn't work at the create_account.jsp file.
As a workaround I create a new exception in the header of the file:
<liferay-ui:error exception="<%=NotOldEnoughException.class%>"
message="Not old enough" />
And made the checking in a hooked version of the EditUserAction file.
the correct way to make the validator would be something like this.
I hope it helps you,
<aui:validator name="custom" errorMessage="Tienes que ser mayor de 18 años">
function (val, fieldNode, ruleValue) {
var val = new Date(Date.parse(val,"MMM dd yyyy"));
var adult=new Date();
adult.setFullYear(adult.getFullYear() - 18)
return (val<adult);
}
</aui:validator>
Related
I'm teaching myself how to code with NodeJS and I'm currently working on a basic task manager project in which users can enter tasks that then get saved into my mongo database. So far, most of it is working and I've been able to store task models with the variables "name", "_id" and "completed", but I can't seem to get it to work when I want to add a date to that as well.
My goal is basically to be able to have users input their own tasks with a name and date that then get stored into my database, after which they get listed into a personal agenda for the user, so getting the date to work is mandatory for my application.
Here's what I have so far:
(relevant portion of) index.html:
<form class="task-form">
<h4>task manager</h4>
<div class="form-control">
<input
type="text"
name="name"
class="task-input"
placeholder="e.g. study"
/>
</div>
<div class="form-control">
<input type="date" name="date" class="task-input" />
</div>
<button type="submit" class="btn submit-btn">submit</button>
<div class="form-alert"></div>
</form>
(relevant portion of) script:
const tasksDOM = document.querySelector(".tasks");
const loadingDOM = document.querySelector(".loading-text");
const formDOM = document.querySelector(".task-form");
const taskInputDOM = document.querySelector(".task-input");
const formAlertDOM = document.querySelector(".form-alert");
// Load tasks from /api/tasks
const showTasks = async () => {
loadingDOM.style.visibility = "visible";
try {
const {
data: { tasks },
} = await axios.get("/api/v1/tasks");
if (tasks.length < 1) {
tasksDOM.innerHTML = '<h5 class="empty-list">No tasks in your list</h5>';
loadingDOM.style.visibility = "hidden";
return;
}
const allTasks = tasks
.map((task) => {
const { completed, _id: taskID, name, date } = task;
return `<div class="single-task ${completed && "task-completed"}">
<h5><span><i class="far fa-check-circle"></i></span>${name}</h5>
<div class="task-links">
// form
formDOM.addEventListener("submit", async (e) => {
e.preventDefault();
const name = taskInputDOM.value;
const date = taskInputDOM.value;
try {
await axios.post("/api/v1/tasks", { name, date });
showTasks();
taskInputDOM.value = "";
formAlertDOM.style.display = "block";
formAlertDOM.textContent = `success, task added`;
formAlertDOM.classList.add("text-success");
} catch (error) {
formAlertDOM.style.display = "block";
formAlertDOM.innerHTML = `error, please try again`;
}
setTimeout(() => {
formAlertDOM.style.display = "none";
formAlertDOM.classList.remove("text-success");
}, 3000);
});
Relevant mongoose schema:
const TaskSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "A task must have a name."],
trim: true,
maxlength: [200, "A task cannot be more than 200 characters."],
},
completed: {
type: Boolean,
default: false,
},
date: {
type: Date,
required: [false, "A task must have a date."],
},
});
One thing I might have been able to spot is that the date input gets handled as a string instead of a date. This is what the chrome console network tab showed after I tried to add a task with the name 'work':
I'm really in the dark here in terms of a solution and I would love to finish this project. If anyone could give me a nudge in the right direction, it would be wildly appreciated!
With kind regards,
Bram
You are using the querySelector for ".task-input" which returns only the first occurence and then you used the same for "name" and "date" variables.
That's why it is taking same values in both.
Instead add an "id" to both of the inputs and select them by id.
index.html
<form class="task-form">
<h4>task manager</h4>
<div class="form-control">
<input
type="text"
name="name"
id="name"
class="task-input"
placeholder="e.g. study"
/>
</div>
<div class="form-control">
<input type="date" name="date" id="date" class="task-input" />
</div>
<button type="submit" class="btn submit-btn">submit</button>
<div class="form-alert"></div>
</form>
While handling form in JS
const tasksDOM = document.querySelector(".tasks");
const loadingDOM = document.querySelector(".loading-text");
const formDOM = document.querySelector(".task-form");
const taskInputDOM = document.querySelector(".task-input");
const formAlertDOM = document.querySelector(".form-alert");
// Load tasks from /api/tasks
const showTasks = async () => {
loadingDOM.style.visibility = "visible";
try {
const {
data: { tasks },
} = await axios.get("/api/v1/tasks");
if (tasks.length < 1) {
tasksDOM.innerHTML = '<h5 class="empty-list">No tasks in your list</h5>';
loadingDOM.style.visibility = "hidden";
return;
}
const allTasks = tasks
.map((task) => {
const { completed, _id: taskID, name, date } = task;
return `<div class="single-task ${completed && "task-completed"}">
<h5><span><i class="far fa-check-circle"></i></span>${name}</h5>
<div class="task-links">
// form
formDOM.addEventListener("submit", async (e) => {
e.preventDefault();
const name = document.getElementById("name").value;
const date = document.getElementById("date").value;
try {
await axios.post("/api/v1/tasks", { name, date });
showTasks();
taskInputDOM.value = "";
formAlertDOM.style.display = "block";
formAlertDOM.textContent = `success, task added`;
formAlertDOM.classList.add("text-success");
} catch (error) {
formAlertDOM.style.display = "block";
formAlertDOM.innerHTML = `error, please try again`;
}
setTimeout(() => {
formAlertDOM.style.display = "none";
formAlertDOM.classList.remove("text-success");
}, 3000);
});
I have no idea what I'm doing wrong, but I cannot get typeahead working in my MVC 5 application. I installed everything via NuGet and my view includes #Scripts.Render("~/bundles/typeahead"), which is rendering properly when viewing the source of the view. So the issue isn't that the dependencies are missing.
I am not seeing any drop down appear when I start typing, and using Fiddler I do not see any calls being made out to the remote that I setup that pulls the data.
Here's the line in my view that typeahead is being attached:
#Html.TextBoxFor(m => m.MainInfo.CompanyName,
new { #class = "form-control typeahead", id = "comp-name", autocomplete="off" })
Here's the portion of my script that configures typeahead and bloodhound:
$(document).ready(function() {
var clients = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "/info/client?like=%QUERY",
wildcard: '%QUERY',
filter: function (clients) {
return $.map(clients, function (client) {
return {
value: client.Name,
clientId: client.Identifier
};
});
}
}
});
clients.initialize();
$('#comp-name').typeahead(null,
{
display: 'value',
minLength: 1,
source: clients.ttAdapter(),
templates: {
empty: "Looks like a new client...",
suggestion: Handlebars.compile("<p><b>{{value}}</b> - {{clientId}}</p>")
}
});
});
Is there something that I've configured wrong in my javascript? I've used a few tutorials as well as their own documentation, but I cannot figure out what I'm doing wrong here. It almost feels like it's not properly initialized, but there are no errors being thrown.
NOTE: Just as an FYI I'm using Bootstrap 3 as well in case that changes anything.
EDIT: Here's my #section Scripts:
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/typeahead")
<script src="#Url.Content("~/Scripts/handlebars.min.js")"></script>
<script src="#Url.Content("~/Scripts/ProjectSetupFormScripts.js")"></script> <-- this is where typeahead is set up
This did the trick for me:
JS
#section Scripts {
<script type="text/javascript">
$(function () {
SetupTipeahead();
});
function SetupTipeahead() {
var engine = new Bloodhound({
remote: {
url: '/Employees/AllEmployees',
ajax: {
type: 'GET'
}
},
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.FullName);
},
queryTokenizer: Bloodhound.tokenizers.whitespace
});
engine.initialize();
$('#FullName').typeahead(null, {
displayKey: 'FullName',
source: engine.ttAdapter(),
templates: {
empty: [
'<div class="empty-message">',
'No match',
'</div>'
].join('\n'),
suggestion: function (data) {
return '<p class="">' + data.FullName + '</p><p class="">' + data.ManNumber + '</p>';
}
}
});
}
</script>
EmployeesController has the following JsonResult
public JsonResult AllEmployees()
{
return Json(db.Employees.ToList(),JsonRequestBehavior.AllowGet);
}
Hello try to wrap your script in #section scripts {} this will place the script at the bottom just before the </body> tag and make sure you are not calling the function before your bundles load.
#section scripts {
<script>
$(document).ready(function() {
var clients = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "/info/client?like=%QUERY",
wildcard: '%QUERY',
filter: function (clients) {
return $.map(clients, function (client) {
return {
value: client.Name,
clientId: client.Identifier
};
});
}
}
});
clients.initialize();
$('#comp-name').typeahead(null,
{
display: 'value',
minLength: 1,
source: clients.ttAdapter(),
templates: {
empty: "Looks like a new client...",
suggestion: Handlebars.compile("<p><b>{{value}}</b> - {{clientId}}</p>")
}
});
});
</script>
}
I have a little problem with my code and need some advice.
I try to simulate a diceroll with Vue.js. To be sure any diceroll is different, i want to create a component for that. I use that code for my app.js
Vue.component('diceroll', {
template: 'This is the result !' + diceroll,
data: function() {
return {
diceroll: 0
}
},
methods: function(){
diceroll: Math.floor(Math.random() * 6) + 1;
}
}
)
var demo = new Vue( {
el: ' #demo',
}
)
Obviously, it don't work and i don't understand how to do that. I read the doc and watch the laracast's series but...
Someone can help me on this ? ^^
"methods" in Vue are actually objects (key-value pair) where the value is a function. Also, inside the template you have to refer variables using mustache binding like this: {{ vName }}.
I made example: (here is a jsbin demo)
Vue.component('diceroll', {
template: 'This is the result: {{diceroll}}',
data: function() {
return {
diceroll: 0
};
},
methods: {
roll: function() {
this.diceroll = Math.floor(Math.random() * 6) + 1;
}
},
ready: function() {
this.roll();
}
});
var demo = new Vue({
el: '#demo'
});
<script src="http://vuejs.org/js/vue.js"></script>
<div id="demo">
<diceroll></diceroll>
</div>
Hy folks, I got a problem with databinding. I tried since ages to figure out why my few has no access to my global.user provided by a service.
Could somebody figure out whats happening. Thank in advance. best regards Thomas
profile.html
<section data-ng-controller="MyprofileController">
<h1>{{global.current_User()}}</h1>
</section>
myprofile.js
'use strict';
angular.module('mean.system').controller('MyprofileController', ['$scope', 'Global', function ($scope, Global) {
$scope.global = Global;
$scope.test = 'testcase';}]);
service
'use strict';
//Global service for global variables
angular.module('mean.system').factory('Global', [
function() {
var current_user = window.user;
return {
current_User: function() {
return current_user;
},
isloggedIn: function() {
return !!current_user;
}
};
}
]);
thanks a lot for your help.
Just found out that firefox does print an error message!
Error: [ng:areq] Argument 'MyprofileController' is not a function, got undefined
http://errors.angularjs.org/1.2.11/ng/areq?
p0=MyprofileController&p1=not%20a%20function%2C%20got%20undefined
minErr/<#http://localhost:3000/lib/angular/angular.js:78
assertArg#http://localhost:3000/lib/angular/angular.js:1363
assertArgFn#http://localhost:3000/lib/angular/angular.js:1374
#http://localhost:3000/lib/angular/angular.js:6774
nodeLinkFn/<#http://localhost:3000/lib/angular/angular.js:6186
forEach#http://localhost:3000/lib/angular/angular.js:310
nodeLinkFn#http://localhost:3000/lib/angular/angular.js:6173
compositeLinkFn#http://localhost:3000/lib/angular/angular.js:5637
publicLinkFn#http://localhost:3000/lib/angular/angular.js:5542
ngViewFillContentFactory/<.link#http://localhost:3000/lib/angular-route/angular-
route.js:915
nodeLinkFn#http://localhost:3000/lib/angular/angular.js:6228
compositeLinkFn#http://localhost:3000/lib/angular/angular.js:5637
publicLinkFn#http://localhost:3000/lib/angular/angular.js:5542
boundTranscludeFn#http://localhost:3000/lib/angular/angular.js:5656
controllersBoundTransclude#http://localhost:3000/lib/angular/angular.js:6248
update#http://localhost:3000/lib/angular-route/angular-route.js:865
Scope.prototype.$broadcast#http://localhost:3000/lib/angular/angular.js:12245
updateRoute/<#http://localhost:3000/lib/angular-route/angular-route.js:556
qFactory/defer/deferred.promise.then/wrappedCallback#http:
//localhost:3000/lib/angular/angu lar.js:10949
qFactory/defer/deferred.promise.then/wrappedCallback#http:
//localhost:3000/lib/angular/angu lar.js:10949
qFactory/ref/<.then/<#http://localhost:3000/lib/angular/angular.js:11035
Scope.prototype.$eval#http://localhost:3000/lib/angular/angular.js:11961
Scope.prototype.$digest#http://localhost:3000/lib/angular/angular.js:11787
Scope.prototype.$apply#http://localhost:3000/lib/angular/angular.js:12067
#http://localhost:3000/lib/angular/angular.js:9202
createEventHandler/eventHandler/<#http://localhost:3000/lib/angular/angular.js:2613
forEach#http://localhost:3000/lib/angular/angular.js:310
createEventHandler/eventHandler#http://localhost:3000/lib/angular/angular.js:2612
<section class="ng-scope" data-ng-view="">
It should work, and it does in a Fiddle I've created: http://jsfiddle.net/BernhardW/mLQWs/
window.user = 'John Doe';
angular.module('mean.system', []);
angular.module('mean.system').controller('MyprofileController', function ($scope, Global) {
$scope.global = Global;
$scope.test = 'testcase';
});
angular.module('mean.system').factory('Global', function() {
var current_user = window.user;
return {
current_User: function() {
return current_user;
},
isloggedIn: function() {
return !!current_user;
}
};
});
Are there any errors showing?
I'm new to backbone.js and express and I have been adapting Christophe Coenraets Wine Cellar REST API example application for my own project.
I am building a form that has several menus needing to be populated from multiple unrelated collections in mongodb.
I am able to populate one menu with one collection, but I have no idea how to get more than one collection to my form View.
Here are the files I am using to populate one menu. How do I expand this to populate two menus?
I suppose I could make a new View for every menu I want to populate - but that seems like overkill.
Can I combine two mongodb find() collections into one object, and list them separately on a page? If so, how?
thanks in advance!
/routes/modules.js contains:
exports.findAllModules = function(req, res) {
db.collection('modules', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
};
/server.js contains:
app.get('/modules', module.findAllModules);
/public/js/main.js contains:
routes: {
"modules" : "list" }
...
list: function(page) {
var p = page ? parseInt(page, 10) : 1;
var moduleList = new ModuleCollection();
moduleList.fetch({success: function(){
console.log('in list function');
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
}});
this.headerView.selectMenuItem('home-menu');
},
...
utils.loadTemplate([
'ModuleListItemView' ], function() {
app = new AppRouter();
Backbone.history.start(); });
/public/models/models.js contains:
window.Module = Backbone.Model.extend({
urlRoot: "/modules",
idAttribute: "_id",
initialize: function () {
this.validators = {};
this.validators.name = function (value) {
return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a name"};
};
validateItem: function (key) {
return (this.validators[key]) ? this.validators[key](this.get(key)) : {isValid: true};
},
validateAll: function () {
var messages = {};
for (var key in this.validators) {
if(this.validators.hasOwnProperty(key)) {
var check = this.validators[key](this.get(key));
if (check.isValid === false) {
messages[key] = check.message;
}
}
}
return _.size(messages) > 0 ? {isValid: false, messages: messages} : {isValid: true};
},
defaults: {
_id: null,
name: ""
} });
window.ModuleCollection = Backbone.Collection.extend({
model: Module,
url: "/modules"
});
/public/js/views/modulelist.js contains:
window.ModuleListView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
} });
window.ModuleListItemView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
return this;
} });
/public/tpl/ModuleListView.html contains:
Not entirely sure how your code works, but here are a few backbone tips.
If you wanna build a menu from a collection don't pass the collection as a model.
Instead of:
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
Use:
$("#content").empty().append(new ModuleListView({collection: moduleList, page: p}).el);
Instead of:
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
}
Use:
render: function () {
this.$el.html('<ul class="thumbnails">');
this.collection.each(function(model) {
this.$('.thumbnails').append(new ModuleListItemView({model: model}).render().el);
}, this);
return this;
}
If you have no need in updating or deleting your models, it's enough to add the url path /modules only to the collection, for reading the initial modules.