I'm unable to attach $watch to a variable to check its validity within a form.
fName.$valid throws error
TypeError: Cannot read property 'exp' of undefined
at watchFnToHumanReadableString
Array : menuItems
{
name : 'Games',
validationRequired : true,
formName : 'games_action'
}
JS
angular.forEach($scope.menuItems, function(item,i) {
if(item.validationRequired) {
var fName = item.formName;
$scope.$watch(fName.$valid, function(validity) { /* throws error */
domeSomething(validity);
})
}
});
I guess its because fName is a String and has no own Property like $valid.
But watching the string fName+'.$valid' should be possible.
$scope.$watch(fName+'.$valid', function(validity) {
console.log('$watcher triggered: ', validity);
})
Related
I am trying to create a design for tags of entities in PouchDB with ReactJS. I managed to save my design using the put function, but when I query my design, the response is just an empty array and I am getting following error in console:
TypeError: Cannot read property 'emit' of undefined
I think the problem is in my function that I later use as a map parameter to my design variable:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
this.db is declared in constructor:
constructor(service, name)
{
if (!service || !name) throw new Error("PouchDatabase initialized incorrectly");
this.name = name;
this.db = new PouchDB(name);
this.service = service;
this.tagsView();
}
Please bare in mind that I am completely new to PouchDB.
Any ideas how can I initialize the emit function?
Thank you in advance.
I assume, that your function is a part of a JavaScript class (otherwise you have to explain the idea with this). In ES6, you have to bind this to your regular functions. You have two options:
First - bind it via constructor:
constructor() {
this.emitTagsMap = this.emitTagsMap.bind(this);
}
Second - declare the function as an arrow one. This way, react will bind it for you:
emitTagsMap = (doc) =>
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
You don't need to call emit over the database object.
Try this:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
emit(x, null);
});
}
}
};
According to the PouchDB docs a design document is formed like this:
// first create a new design doc and pass your map function as string into it
var ddoc = {
_id: "_design/my_index",
views: {
by_name: {
map: "function (doc) { if (doc !== undefined) { if (Array.isArray(doc.tags)) { doc.tags.forEach(x => { emit(x, null); }); } } }"
}
}
};
// save it
db.put(ddoc).then(function () {
// success!
}).catch(function (err) {
// some error (maybe a 409, because it already exists?)
});
//Then you actually query it, by using the name you gave the design document when you saved it:
db.query('my_index/by_name').then(function (res) {
// got the query results
}).catch(function (err) {
// some error
});
https://pouchdb.com/guides/queries.html
I'm trying to copy an array to another array and I'm getting that error:
[Vue warn]: Error in render: "TypeError: Cannot read property 'name'
of undefined"
My code:
props: {
languages: {
required: true,
},
},
data() {
return {
translations: [],
}
},
mounted() {
this.setTranslations();
},
methods: {
setTranslations() {
this.translations = this.languages.slice(0);
}
},
Same result with:
this.translations = this.languages
and:
let temp = this.languages.slice(0);
this.translations = temp;
EDIT
If I comment that line:
// this.translations = this.languages.slice(0);
Error disappears.
This also does not work:
this.languages.forEach( function (item) {
this.translations.push(item);
});
I'm getting error:
Error in mounted hook: "TypeError: Cannot read property 'translations'
of undefined"
But this works:
let temp = this.languages.slice(0);
temp.forEach( function ( lang ) {
Vue.set(lang, 'value', {});
Vue.set(lang.value, 'name', "");
Vue.set(lang.value, 'metaKeywords', "");
Vue.set(lang.value, 'metaDescription', "");
});
this.translations = temp;
Although that way my languages array became the same as translations array, which is not what I want.
What I'm doing wrong?
Actually this error comes from template. Template depends on "name" in my translations array. And it cant find it. That's why my last code works.
I don't know why console does not show error when I comment array copy line. It should show the same error.
I have successfully used $watch over the form ngModel Controller $error attribute to watch for the invalid state then add custom error message to the HTML element tooltip (title attribute) when the validity state becomes invalid.
Following is the code:
var addValidationMessage = function (ngForm, elm, errAttr, errMsg, msgVar1) {
//Use $timeout to ensure validation rules are added and compiled.
//After compile is done then will start watching errors
$timeout(function(){
var elmModel;
var ngModelName="";
//Get the name of the 'ng-model' of the element being validated
elmModel = angular.element(elm).controller('ngModel');
if (elmModel && elmModel.$name) {
ngModelName = elmModel.$name;
}
if (!ngModelName) {
ngModelName = angular.element(elm).attr('ng-model');
}
if (ngModelName) {
scope.$watch(ngForm.$name + '.' + ngModelName + '.$error.' + errAttr,
function (newValue, oldValue){
//console.log("elm.id =", elm.id);
//The validation error message will be placed on the element 'title' attribute which will be the field 'tooltip'.
//newValue == true means there is error
if (newValue) {
var msgVar1Val;
//Perform variable substitution if required to get the final text of the error message.
if (msgVar1) {
msgVar1Val = scope.$eval(angular.element(elm).attr(msgVar1));
errMsg = errMsg.format(msgVar1Val);
}
//Append the error to the title if neeeded
if (elm.title) {
elm.title += " ";
} else {
elm.title = "";
}
elm.title += errMsg;
} else {
//Remove the error if valid.
//child.removeAttribute('title');
if (elm.title) {
//Replace the error message with blank.
elm.title = elm.title.replace(errMsg, "").trim();
}
}
});
} else {
//console.warn("Warning in addValidationMessage() for element ID '%s' in ngForm '%s'. Message: 'ng-model' is not defined.", elm.id, ngForm.$name)
}
}, 1000);
}
And, the above function is used within the element directive as follows:
errMsg = "Number of characters entered should not exceed '{0}' characters.";
addValidationMessage(ngForm, child, 'maxlength', errMsg, 'ng-maxlength');
The above code is causing performance issues when processing 1000+ fields. In other words, it causes the execution in Chrome to slow down by 10 seconds for 1000+ fields. In IE it causes processing to slow down by 1 minute.
The prupose
Is there better approach to achieve the same objective?
Appreciate your feedback.
Tarek
In my Angular 2 application I have a function :
notification : Array<any>;
......
......
getNotification () {
return setTimeout(() => {
this.AppbankService.notify(this.user.NameId)
.subscribe(
(response) => {
if (response.status === 200) {
this.notifications.push(response.json());
console.log(typeof(this.notifications));
}
this.getNotification();
}
)
},5000)}
In this function, I get notification from the server every 5 seconds and try to push them to an array, but a have this:
error app.module.ts:104 error : TypeError: Cannot read property 'push' of undefined(…)
Any suggestion?
Change
notification : Array<any>;
to
notification : Array<any> = [];
I had the same issue of push string message and my issue has resolved by below code.
messages: Array<string> = [];
add(message: string): void {
this.messages.push(message);
}
I faced the same issue and then discovered that I forgot to initialize my service method itself and its type was set to any.
I'm using Backbone to manage the state of an HTML form. The Model's role is to handle validation. The View's role is to wrap the HTML form and respond to the change or error events emitted by the model.
Backbone seems to only emit change events when the given field is actually valid. This is causing some really unexpected behavior that makes me thing that I'm doing this wrong.
Here is a summary of what I'm doing:
1. Initial load serializes the form and injects it into the model
2. When an error event is emitted, I generate error nodes next to the invalid field.
3. When a change event is emitted, I remove the error notes next to the (now valid) field.
When a page is rendered with an initially valid form, and a user invalidates a field, the message is displayed as expected; however, the model never updates the field internally. Thus when the user corrects the error, a change event is never emitted.
Example: Initially valid
When a page is rendered with an initially invalid form, things appear to be working fine... but this is only because the model's initial attributes are empty. Correcting the field makes the messages disappear, but if you change it again to an invalid state, the message never disappears.
Example: Initially invalid
What am I doing wrong? Perhaps there's another approach I should be using instead?
My Model
var Foo = Backbone.Model.extend({
validate: function(attr) {
var errors = {};
if (_.isEmpty(attr)) return;
if (attr.foo && attr.foo != 123) {
errors.foo = ['foo is not equal to 123'];
}
if (attr.bar && attr.bar != 456) {
errors.bar = ['bar is not equal to 456'];
}
return _.isEmpty(errors) ? undefined : errors;
}
});
My View
FooForm = Backbone.View.extend({
events: {
'change :input': 'onFieldChange'
},
initialize: function(options) {
this.model.on('error', this.renderErrors, this);
this.model.on('change', this.updateFields, this);
// Debugging only
this.model.on('all', function() {
console.info('[Foo all]', arguments, this.toJSON())
});
this.model.set(this.serialize());
},
onFieldChange: function(event) {
var field = event.target,
name = field.name,
value = field.value;
this.model.set(name, value);
},
renderErrors: function(model, errors) {
_.each(errors, function(messages, fieldName) {
var el = $('#' + fieldName),
alert = $('<div/>').addClass('error');
el.parent().find('.error').remove();
_.each(messages, function(message) {
alert.clone().text(message).insertAfter(el);
});
});
},
updateFields: function(model, options) {
if (!options || !options.changes) return;
_.each(_.keys(options.changes), function(fieldName) {
var el = $('#' + fieldName);
el.parent().find('.error').remove();
});
},
serialize: function() {
var raw = this.$el.find(':input').serializeArray(),
data = {},
view = this;
$.each(raw, function() {
// Get the model's field name from the form field's name
var name = this.name;
if (data[name] !== undefined) {
if (!data[name].push) {
data[name] = [data[name]];
}
data[name].push(this.value || '');
}
else {
data[name] = this.value || '';
}
});
return data;
}
});
You can't validate individual field using native Backbone validation.
In my app I use this validation plugin: https://github.com/thedersen/backbone.validation
Then in your model you add validation rules per each field (it's optional, so you don't need to add this to all models):
var NewReview = Backbone.Model.extend({
initialize: function() {
/* ... */
},
validation: {
summary: {
required: true,
minLength: 10
},
pros: {
required: true,
minLength: 10
},
cons: {
required: true,
minLength: 10
},
overall: function(value) {
var text = $(value).text().replace(/\s{2,}/g, ' ');
if (text.length == 0) text = value;
if (text.length < 20) return "Overall review is too short";
},
rating: {
range: [0.5, 5]
},
product_id: {
required: true
}
}
});
Than in views or elsewhere you can validate either entire model or individual fields:
if (this.model.validate()) { ... }
or
if (this.model.isValid("summary")) { ... }