I am using a validation plug-in based on jQuery validation in my AngularJS app (which is built on top of a jQuery library).
When I use ui bootstrap Typeahead with validation the search results are misplaced.
Plunkr:
http://plnkr.co/edit/ZYP58GxITghkTqE7PNHy
HTML (help.html)
<div class="form-group">
<label for="category">Category "{{formData.category}}"</label>
<input class="form-control" type="text" name="category" id="category" placeholder="Search..." ng-model="formData.category" typeahead="obj.name for obj in getCdOnCat($viewValue)" typeahead-editable="false" typeahead-loading="loadingLocations" required>
</div>
JS (script.js) - HelpController
//Typeahead: Category Search
$scope.getCdOnCat = function (searchVal) {
return dataFactory.getCdOnCategory(searchVal).then(function (response) {
return response.data.categories;
}, function (error) {
console.log('Error: dataFactory.getCdOnCategory');
});
};
$scope.$watch('formData.category', function (value) {
if (value === "No matching categories") {
$scope.formData.category = "";
}
});
Related
I have a directive that makes use of jquery events on the element parameter of the link function, this directive has an input that is binding to a value that is obtained from the main controller of the page, passed through nested directives in a isolated scope , but when changing the value in the input is not reflected in the original object from controller.
The object has the following structure:
Invoice 1:
- Product 1
- Product 2
Invoice 2:
- Product 3
- Product 4
When I change the amount of the invoice, the value is updated in the main controller, but when I change the amount of the product the change is not reflected.
This is my directive, what you should do is that when the user clicks on the value an input appears to be able to edit the value of the model:
eFieldTemplate.html
<div>
<div ng-if="IsMouseIn">
<input type="text" ng-model="value" class="form-control input-sm" />
</div>
<div ng-if="IsMouseOut" ng-click="OnMouseClick()">
{{value}}
</div>
<div ng-if="MouseClick">
<input type="text" ng-model="value" class="form-control input-sm" />
</div>
eFieldDirective.js
angular.module("appDirectives").directive("eField", function () {
return {
restrict: "E",
templateUrl: "eFieldTemplate.html",
scope: {
value: "="
},
controller: function ($scope) {
$scope.IsMouseOut = true;
$scope.IsMouseIn = false;
$scope.MouseClick = false;
$scope.OnMouseEnter = function () {
if (!$scope.MouseClick) {
$scope.IsMouseOut = false;
$scope.IsMouseIn = true;
$scope.MouseClick = false;
}
}
$scope.OnMouseLeave = function () {
if (!$scope.MouseClick) {
$scope.IsMouseOut = true;
$scope.IsMouseIn = false;
$scope.MouseClick = false;
}
}
$scope.OnMouseClick = function () {
$scope.IsMouseOut = false;
$scope.IsMouseIn = false;
$scope.MouseClick = true;
}
$scope.EndEdit = function () {
$scope.IsMouseOut = true;
$scope.IsMouseIn = false;
$scope.MouseClick = false;
}
},
link: function (scope, el, attrs) {
el.on("mouseenter", function () {
scope.OnMouseEnter();
scope.$apply();
});
el.on("mousemove", function () {
scope.OnMouseEnter();
scope.$apply();
});
el.on("mouseleave", function () {
scope.OnMouseLeave();
scope.$apply();
});
el.on("click", function () {
scope.OnMouseClick();
if (el[0].querySelector('input'))
el[0].querySelector('input').select();
scope.$apply();
});
}
};
});
Any Suggestions?
I give the example here: Plunker
UPDATED
I found a solution using ngIf, and is to reference a variable from the parent scope using $ parent.value. Eg.
<Input type="text" ng-model="$parent.value" class="form-control input-sm" />
Or also referring to another object eg.
<input type="text" ng-model="value">
<div ng-if="IsMouseIn">
<input type="text" ng-model="value">
</div>
Here is the reference link: what is the difference between ng-if and ng-show/ng-hide
using ng-if makes it create/destroy new html nodes and it seems to be unable to cope with that. change to ng-show and it will work. i also added a body mouse capture so it ends the edit.
<div>
<div ng-show="IsMouseIn">
<input type="text" ng-model="value" class="form-control input-sm" />
</div>
<div ng-show="IsMouseOut" ng-click="OnMouseClick()">
{{value}}
</div>
<div ng-show="MouseClick">
<input type="text" ng-model="value" class="form-control input-sm" />
</div>
view plunker
If you want to use ng-if not ng-show still, define $scope.values and $scope.config and use like this. To avoid the ng-if problem you should define an object.
<div>
<div ng-if="config.IsMouseIn">
<input type="text" ng-model="values.value" class="form-control input-sm" />
</div>
<div ng-if="config.IsMouseOut" ng-click="OnMouseClick()">
{{values.value}}
</div>
<div ng-if="config.MouseClick">
<input type="text" ng-model="values.value" class="form-control input-sm" />
</div>
*Disclaimer: this question is not about using material design in an angular app but using material design lite inside a form. So, please, don't answer I should rather use angular material, materialize, lumx, material bootstrap, or daemonite... I know, they exist.*
With Angular a typical form field for a name would be:
<form name="myForm">
<label>
Enter your name:
<input type="text"
name="myName"
ng-model="name"
ng-minlength="5"
ng-maxlength="20"
required />
</label>
<div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
<div ng-message="required">You did not enter a field</div>
<div ng-message="minlength">Your field is too short</div>
<div ng-message="maxlength">Your field is too long</div>
</div>
</form>
With Material Design Lite, it would be something like that:
<form action="#">
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" id="user" pattern="[A-Z,a-z, ]*" />
<label class="mdl-textfield__label" for="user">User name</label>
<span class="mdl-textfield__error">Letters and spaces only</span>
</div>
</form>
Question: how is it possible to use the angular validation functionality combined with ngMessage (for multiple error messages) with the Material Design Lite?
You can write your own angular module to validate MDL input fields, here is a working example: http://codepen.io/alisterlf/pen/ZGgJQB
JS
// create angular app
var validationApp = angular.module('validationApp', ['fieldMatch']);
//Field Match directive
angular.module('fieldMatch', [])
.directive('fieldMatch', ["$parse", function($parse) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var me = $parse(attrs.ngModel);
var matchTo = $parse(attrs.fieldMatch);
scope.$watchGroup([me, matchTo], function(newValues, oldValues) {
ctrl.$setValidity('fieldmatch', me(scope) === matchTo(scope));
}, true);
}
}
}]);
//Run material design lite
validationApp.run(function($rootScope, $timeout) {
$rootScope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
componentHandler.upgradeAllRegistered();
}, 0);
});
$rootScope.render = {
header: true,
aside: true
}
});
// create angular controller
validationApp.controller('mainController', function($scope) {
$scope.formStatus = '';
// function to submit the form after all validation has occurred
$scope.submit = function() {
// check to make sure the form is completely valid
if ($scope.form.$invalid) {
angular.forEach($scope.form.$error, function(field) {
angular.forEach(field, function(errorField) {
errorField.$setTouched();
})
});
$scope.formStatus = "Form is invalid.";
console.log("Form is invalid.");
} else {
$scope.formStatus = "Form is valid.";
console.log("Form is valid.");
console.log($scope.data);
}
};
});
I am creating a custom filter in angular js this is my html,
<div class="box-body">
<div class="form-group">
<label>Page-Title:</label>
<input type="text" required value="" data-ng-model="title" name="page_title" class="form-control" id="" placeholder="Enter Page Title">
</div>
<div class="form-group">
<label>Page-Alias:</label>
<input type="text" value="#{{ title | replaceSpace}}" name="page_alias" class="form-control" id="" placeholder="Auto-Generated If Left Blank">
</div>
This is my angular js code
var app = angular.module('CustomAngular', []);
app.controller('CustomCtrl', function ($scope) {
app.filter('replaceSpace', function () {
return function (input) {
return input.replace(/ /g, '-').toLowerCase();
};
});
});
The filter is not working and also I get error in the console.
Error: [$injector:unpr] http://errors.angularjs.org/1.3.15/$injector/unpr?p0=slugifyFilterProvider%20%3C-%20slugifyFilter
at Error (<anonymous>)
If I use filter: infront of the filter name I donot get any errors in console but it's still not working.
<input type="text" value="#{{ title | filter:replaceSpace }}" name="page_alias" class="form-control" id="" placeholder="Auto-Generated If Left Blank">
You don't put filters inside of a controller, put it after your var app line. It's a separate thing just like controllers / directives / etc.
var app = angular.module('CustomAngular', []);
// Here's where it goes
// Also now any/all controllers can use it!
app.filter('replaceSpace', function () {
return function (input) {
return input.replace(/ /g, '-').toLowerCase();
};
});
app.controller('CustomCtrl', function ($scope) {
});
You should define filter outside of controller and not use filter: as it has different meaning, that's the correct way to use your filter
<input type="text" value="#{{ title | replaceSpace }}" name="page_alias" class="form-control" id="" placeholder="Auto-Generated If Left Blank">
although you should either initialize model by $scope.title = '' or place a check in your filter to run replace only when input is defined, otherwise you will get JS errors
filter: is used to filter the array from model, and when you pass another filter to it it does nothing
I am trying to add a "hidden" field to a basic form in Angular (using Firebase as the backend). I'm having trouble figuring out how to include this field as part of the array when the form is submitted. I want to include {type: 'Basic'} as part of the array. I've looked at the other related posts on this site, but am still unsure how to apply to my particular situation.
Any suggestions on how to do this?
Javascript:
myApp.controller('NewProjectCtrl', function ($location, Projects) {
var editProject = this;
editProject.type = 'Basic'; //this is the hidden field
editProject.save = function () {
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
});
HTML:
<form>
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="editProject.project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="editProject.project.description"></textarea>
<button ng-click="editProject.save()" class="btn btn-primary">Save</button>
</form>
You don't need a hidden form field, just submit your value in your controller like this:
editProject.save = function () {
editProject.project.type = 'Basic';
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
All attributes of your editProject.project will be submitted, as you may notice in the developer console.
I would structure the controller a bit different.. here is an example (I am considering you are using angular-resource, where Projects returns a Resource?):
myApp.controller('NewProjectCtrl', function ($location, Projects) {
$scope.project = new Projects({type: 'Basic'});
$scope.save = function () {
$scope.project.$save().then(function(data) {
$location.path('/');
});
};
});
<form ng-submit="save()">
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="project.description"></textarea>
<input type="submit" value="Save" class="btn btn-primary" />
</form>
The save function will $save the new project resource (this is an default method and will make a POST on the given resource URL).
I am trying to do an autocomplete search using UI Bootstrap Typeahead to select a category from a database.
I have set up an example database with 4 categories, the final one will contain more than a thousand categories.
When I start typing, the search results return 4 rows (so something must be working, because I tried to change the number of rows in the DB and the search result mirrors the total number of rows) but with no value. However, it doesn't matter what I write so it doesn't seem to be matching.
HTML
<input class="form-control" type="text" name="category" id="category" placeholder="Search..." ng-model="asyncSelected" typeahead="name for name in getCdOnCat($viewValue)" typeahead-loading="loadingLocations" required>
controller.js
//Typeahead: Category Search
$scope.getCdOnCat = function (searchVal) {
return dataFactory.getCdOnCategory(searchVal, globalVal_accessToken, globalVal_storeId).then(function (response) {
console.log(response.data.categories);
return response.data.categories;
}, function (error) {
console.log('Error: dataFactory.getCdOnCategory');
});
};
service.js
app.factory("dataFactory", function ($http) {
var factory = {};
factory.getCdOnCategory = function (searchVal, accessToken, storeId) {
return $http.get('data/getCdOnCategory.aspx?searchVal=' + searchVal + '&accessToken=' + accessToken + '&storeId=' + storeId)
};
return factory;
});
JSON
{ "categories":[
{ "name": "Clothes" },
{ "name": "Cypress" },
{ "name": "Citrus" },
{ "name": "Cats" }
] }
Please see here http://plnkr.co/edit/F4n1kNOHfZ9f3Zz63x2P?p=preview
change
<input class="form-control" type="text" name="category" id="category"
placeholder="Search..." ng-model="asyncSelected"
typeahead="name for name in getCdOnCat($viewValue)" typeahead-loading="loadingLocations"
required>
to
<input class="form-control" type="text" name="category" id="category"
placeholder="Search..." ng-model="asyncSelected"
typeahead="obj.name for obj in getCdOnCat($viewValue)" typeahead-loading="loadingLocations"
required>