angularJS + jQuery jeditable plugin work together - javascript

I am trying to write a directive for the jeditable plugin so when it changes the value, it will change also edit the model of the edited element.
So i wrote something like that, JS Fiddle
but i don`t know how to get the object that bound to the object in the list.
JS:
var app = angular.module("app", []);
app.controller('ctrl', function ($scope) {
$scope.lst = [{
id: 1,
name: "item1"
}, {
id: 1,
name: "item1"
}, {
id: 2,
name: "item2"
}, {
id: 3,
name: "item3"
}, {
id: 3,
name: "item3"
}];
});
app.directive('uiEditable', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.editable("/echo/json/", {
onblur: 'submit',
onsubmit: function (response, settings) {
//here i need to update the model
}
});
}
};
});

This uses ngModel to update back to the model. (so don't forget ng-model on element)
app.directive('uiEditable', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return; // do nothing if no ng-model
element.editable(function (val) {
var tVal = $.trim(val);
if (ngModel.$viewValue !== tVal)
scope.$apply(function () { return ngModel.$setViewValue(tVal); });
return tVal;
});
}
};
});

Why are you using the jeditable plugin? This plugin seems to only duplicate in jQuery what you could already do in angular using ng-model alone and no plugin required.
If you just want to create text which can be edited in place like jEditable does, instead of creating a custom directive simply using ng-submit, ng-click, ng-hide and ng-model. Here's a rough example.
The view:
<form ng-submit="submit()">
<div ng-hide="showEdit"
ng-click="showEdit = true">
{{foo.bar}}
</div>
<div>
<input type="text"
ng-show="showEdit"
ng-model="foo.bar" />
</div>
<a href="#" ng-show="showEdit"
ng-click="submit();">done</a>
</form>
And the controller:
app.controller('myCtrl', function($scope) {
$scope.foo = {
bar: 'some text'
};
$scope.showEdit = false;
$scope.submit = function() {
// hide the edit field
$scope.showEdit = false;
// submit form
console.log('submit form');
}
});

Pass your item in in an isolated scope:
app.directive('uiEditable', function(){
return {
restrict: 'A',
scope: {
item: '='
},
link: function(scope, element, attrs){
element.editable("/echo/json/", {
onblur: 'submit',
onsubmit: function(response, settings){
alert(scope.item);
}
});
}
};
});
'scope.item' will now give you a reference to the item inside your directive.

Related

Custom owl carousel directive

I tried to convert the normal jQuery own carousel into Angular Directive. It doesn't work for me, show some angular errors which I couldn't find what is the issue.
Controller
$scope.defaultsCarousel = {
'items': 4,
'itemWidth': 300,
'itemsDesktop': [1260, 3],
'itemsTablet': [930, 2],
'itemsMobile': [620, 1],
'navigation': true,
'navigationText': false
};
HTML (Jade)
custom-carousel(data-options="{{ defaultsCarousel }}", productid="#pl-1")
Directive
myApp.directive('customCarousel', function(){
function nextSlide(e) {
e.preventDefault();
e.data.owlObject.next();
};
function prevSlide(e) {
e.preventDefault();
e.data.owlObject.prev();
};
return{
restrict: 'E',
scope: {},
link: function($scope, el, attrs){
var options = $scope.$eval($(el).attr('data-options'));
var product_id = attrs.productid;
console.log(product_id);
$(product_id).owlCarousel(options);
var owl = $(product_id).data('owlCarousel');
$(product_id).parent().find('.slide-control.right').on('click', {owlObject: owl}, nextSlide);
$(product_id).parent().find('.slide-control.left').on('click', {owlObject: owl}, prevSlide);
}
}
ERROR
Syntax Error: Token '{' invalid key at column 2 of the expression [{{] starting at [{4}].
Your problem is at this line $scope.$eval($(el).attr('data-options'));. This produce a parse syntax error. You have two options to fix it:
OPTION 1: get the options from attrs parameter of link directive function. (PLUNKER)
app.directive('customCarousel', function() {
return {
restrict: 'E',
link: function(scope, el, attrs) {
var options = angular.fromJson(attrs.options);
var product_id = attrs.productid;
//..Rest of your logic
}
}
});
OPTION 2: get the options using scope one way binding. (PLUNKER)
app.directive('customCarousel', function() {
return {
restrict: 'E',
scope: {
options: '#',
productid: '#'
},
link: function(scope, el, attrs) {
var options = angular.fromJson(scope.options);
var product_id = scope.productid;
//..Rest of your logic
}
}
});
As you can see I'm getting the html data-options attribute as just options. That's because angularjs directives will ignore data-* prefix in all HTML elements and attributes names.
More info:
Check this post about difference between ng-app and data-ng-app
Check basic data-* prefix docs in W3Schools

Form $validators without ng-model

I have a form element:
<form name="formDate">
<input date-picker name="fundacao" ng-model-date="fTit.fundacao" required>
</form>
And the directive:
app.directive('datePicker', function(){
return {
restrict: 'A',
scope: {
ngModelDate: "=ngModelDate"
},
link: function (ng, el, attr) {
// $validators HERE
}
};
})
The "required" validation does not work because there is no ng-model directive on the element.
Is there any way to validate a form with nonstandard directives, such as the example above?
you can bind change function on element
app.directive('datePicker', function(){
return {
restrict: 'A',
scope: {
ngModelDate: "=ngModelDate"
},
link: function (ng, el, attr,ctrl) {
el.bind('change',function(){
//check the $(el).val();
var isValid= the date is valid;
ctrl.$setValidity('date',isValid);
// the first param is the validation key, the second param is true if valid false if invalid
})
}
};
})

Change jQuery Knob Min/Max value

I'm working on an angular wrapper for the jquery-knob widget. The following works as long as the maximum value doesn't change. If it does, the ng-model binding is lost. If I don't destroy the knob widget at the beginning of the watch, the max value doesn't change.
//Directive
app.directive('knobWidget', function () {
return {
scope: {
maxbinding: "=maxbinding",
maxbindingprop: "#maxbindingprop"
},
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
ngModel.$render = function () {
$(elem).val(ngModel.$viewValue).trigger("change");
};
scope.$watch('maxbinding', function (newVal) {
$(elem).knob('destroy');
$(elem).knob({
min: 0,
max: scope.maxbinding[scope.maxbindingprop],
value: ngModel.$viewValue,
change: function (changeVal) {
scope.$apply(function () {
ngModel.$setViewValue(changeVal);
});
}
});
});
}
};
});
//Markup
<input knob-widget data-min="0" maxbinding="arr" maxbindingprop="length" ng-model="currentStop" />
Doing:
$(elem).knob('max', scope.maxbinding[scope.maxbindingprop]);
doesn't work either. Any ideas?
Using trigger('configure') followed by trigger('change') should do the trick
$(elem).trigger('configure', {
'max': scope.maxbinding[scope.maxbindingprop];
});
$(elem).trigger('change');
Source

angular.js binding jquery autocomplete object to ng-model, input value to property of object

I am using in my application angular.js and jquery autocomplete.
So, I would like to create an angular directive, which wrap jquery autocomplete:
'use strict'
angular.module('nsi')
.directive('autoComplete', function () {
return {
restrict: 'A',
scope: {
httpService: "=",
renderItem: '=',
ngModel: '=',
minLength: '=',
onSelect: '='
},
link: function (scope, elem) {
elem.autocomplete({
source: function (request, response) {
scope.httpService(request.term).then(function (data) {
response(
$.map(data.items, function (item) {
return scope.renderItem(item);
}
)
);
});
},
minLength: scope.minLength,
select: function (event, ui) {
if (scope.onSelect) {
scope.onSelect(ui.item.item);
}
scope.$apply(function () {
scope.ngModel = ui.item.item;
});
}
})
;
}
};
});
in my controller I initialize necessary parameters for it:
$scope.supplierRender = function(item){
return {
label: item.supplierName,
value: item.supplierName,
item: item
}
};
$scope.httpSupplierService = function(suggest){
return SupplierService.getSuppliers('%' + suggest + '%');
};
$scope.supplierSelect = function(val) {
$scope.employee.supplier.id = val.id;
};
In my html view I start my directive:
<input type="text"
auto-complete
min-length="3"
http-service="httpSupplierService"
render-item="supplierRender"
on-select="supplierSelect"
ng-model="employee.supplier"
class="form-control"
value="employee.supplier.supplierName" />
The problem is, that when I am opening my employee in edit window, in autocomplete input i see [object Object]. So the queston is: "How to bind ngModel to employee.Supplier", and value of input to specific field of object (employee.supplier.supplierName)
Try to set ng-model:
ng-model="employee.supplier.supplierName"
Generally <input> takes value from directive ng-model so you don't need provide value=..
This is a test: Fiddle
The value=.. attribute works when ng-model not defined for input

AngularJS: how to bind a constant object to a directive

I've created a directive with a binding using "scope". In some cases, I want to bind a constant object. For instance, with HTML:
<div ng-controller="Ctrl">
<greeting person="{firstName: 'Bob', lastName: 'Jones'}"></greeting>
</div>
and JavaScript:
var app = angular.module('myApp', []);
app.controller("Ctrl", function($scope) {
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: {
person: "="
},
template:
'<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
Although this works, it also causes a JavaScript error:
Error: 10 $digest() iterations reached. Aborting!
(Fiddle demonstrating the problem)
What's the correct way to bind a constant object without causing the error?
Here's the solution I came up with, based on #sh0ber's answer:
Implement a custom link function. If the attribute is valid JSON, then it's a constant value, so we only evaluate it once. Otherwise, watch and update the value as normal (in other words, try to behave as a = binding). scope needs to be set to true to make sure that the assigned value only affects this instance of the directive.
(Example on jsFiddle)
HTML:
<div ng-controller="Ctrl">
<greeting person='{"firstName": "Bob", "lastName": "Jones"}'></greeting>
<greeting person="jim"></greeting>
</div>
JavaScript:
var app = angular.module('myApp', []);
app.controller("Ctrl", function($scope) {
$scope.jim = {firstName: 'Jim', lastName: "Bloggs"};
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: true,
link: function(scope, elements, attrs) {
try {
scope.person = JSON.parse(attrs.person);
} catch (e) {
scope.$watch(function() {
return scope.$parent.$eval(attrs.person);
}, function(newValue, oldValue) {
scope.person = newValue;
});
}
},
template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
You are getting that error because Angular is evaluating the expression every time. '=' is for variable names.
Here are two alternative ways to achieve the same think without the error.
First Solution:
app.controller("Ctrl", function($scope) {
$scope.person = {firstName: 'Bob', lastName: 'Jones'};
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: {
person: "="
},
template:
'<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
<greeting person="person"></greeting>
Second Solution:
app.directive("greeting2", function () {
return {
restrict: "E",
replace: true,
scope: {
firstName: "#",
lastName: "#"
},
template:
'<p>Hello {{firstName}} {{lastName}}</p>'
};
});
<greeting2 first-name="Bob" last-Name="Jones"></greeting2>
http://jsfiddle.net/7bNAd/82/
Another option:
app.directive("greeting", function () {
return {
restrict: "E",
link: function(scope,element,attrs){
scope.person = scope.$eval(attrs.person);
},
template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
This is because if you use the = type of scope field link, the attribute value is being observed for changes, but tested for reference equality (with !==) rather than tested deeply for equality. Specifying object literal in-line will cause angular to create the new object whenever the atribute is accessed for getting its value — thus when angular does dirty-checking, comparing the old value to the current one always signals the change.
One way to overcome that would be to modify angular's source as described here:
https://github.com/mgonto/angular.js/commit/09d19353a2ba0de8edcf625aa7a21464be830f02.
Otherwise, you could create your object in the controller and reference it by name in the element's attribute:
HTML
<div ng-controller="Ctrl">
<greeting person="personObj"></greeting>
</div>
JS
app.controller("Ctrl", function($scope)
{
$scope.personObj = { firstName : 'Bob', lastName : 'Jones' };
});
Yet another way is to create the object in the parent element's ng-init directive and later reference it by name (but this one is less readable):
<div ng-controller="Ctrl" ng-init="personObj = { firstName : 'Bob', lastName : 'Jones' }">
<greeting person="personObj"></greeting>
</div>
I don't particularly like using eval(), but if you really want to get this to work with the HTML you provided:
app.directive("greeting", function() {
return {
restrict: "E",
compile: function(element, attrs) {
eval("var person = " + attrs.person);
var htmlText = '<p>Hello ' + person.firstName + ' ' + person.lastName + '</p>';
element.replaceWith(htmlText);
}
};
});
I had the same problem, I solved it by parsing the json in the compile step:
angular.module('foo', []).
directive('myDirective', function () {
return {
scope: {
myData: '#'
},
controller: function ($scope, $timeout) {
$timeout(function () {
console.log($scope.myData);
});
},
template: "{{myData | json}} a is {{myData.a}} b is {{myData.b}}",
compile: function (element, attrs) {
attrs['myData'] = angular.fromJson(attrs['myData']);
}
};
});
The one drawback is that the $scope isn't initially populated when the controller first runs.
Here's a JSFiddle with this code.

Categories