I'm super new to Angular and JS in general. Basically I have an array thats stored in a var inside a factory and I want that entire array (this.insights) to get imported(?) into my directive.
I believe I need to do something like scope.insights = [];
var Piece = function (parameters) {
this.thing = null;
this.that = null;
this.insights = [
{
id: 1,
}
{
id: 2,
}
];
}
Inject this factory in directive same way you inject other services.
app.directive('helloWorld', function(Piece) {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>',
controller: function(){
var kek = Piece.insights;
}
};
});
Related
I have two directives in my code where parent directive's template contains child directive. The child directive is passed an attribute called "items" and it calls a function which will be returning a promise.
The child directive's controller then takes that items promise and does a 'then' to get the results. These results are then to be displayed in the dropdown.
I have been working with this code for a while now and am not able to figure out the warning I am getting. Also, I am not getting the right results.
Basically, when you click on Change Value button, it will return a different array. Odd numbers and event numbers provide different arrays (this was so I could simulate change to the model).
Below is the code and here is a link to the Codepen
Parent Controller
app.controller("mainCtrl", function($scope, $q) {
var that = this;
that.r = 0;
var i = [{ name: "toronto" }, { name: "chicago" }, { name: "new york" }];
var j = [{ name: "Martin" }, { name: "Stephanie" }, { name: "Kirk" }];
this.getItems = function () {
var deferred = $q.defer();
if (that.r % 2 == 0) {
console.log("getItems: i returned");
deferred.resolve(i);
} else {
console.log("getItems: j returned");
deferred.resolve(j);
}
return deferred.promise;
};
this.changeVal = function() {
that.r += 1;
console.log(that.r);
};
});
Parent Directive
app.directive("parentDirective", function() {
return {
template:
'<child-directive items="ct.getItems()"></child-directive><button ng-click="ct.changeVal()">Change Value</button>',
controller: "mainCtrl",
controllerAs: "ct",
scope: {}
};
});
Child Controller
app.controller("childCtrl", function($scope) {
$scope.name = "Child controller";
$scope.returnedItems =[];
$scope.items.then(function(result){
console.log("Child controller THEN from promise executed");
$scope.returnedItems = result;
});
});
Child Directive
app.directive("childDirective", function() {
return {
template:
'<select ng-model="selectedAction" ng-options="x.name for x in returnedItems"><option value="">Select</option></select>',
controller: "childCtrl",
scope: {
items: "<"
}
};
});
when I run the code above, I do get the drop-down populated. But it doesn't change after.
Appreciate any help on this. Thanks!
At the part in your child directive here,
items="ct.getItems()"
You are giving the child directive's scope the result of that function. In this case, a promise. When this promise gets resolved your child scope sets the ng-options to the result as expected. However subsequent calls to ct.getItems() creates a new promise that your child directive knows nothing about.
In my opinion, the cleanest way to get your results into the child is to pass the items array into the child directive. And whenever getItems() is called from the parent directive, update the results array. It simplifies a lot of the code. Here's your modified code. The main thing to note is that the child directive binds to currentItems array instead of the promise, and the array is updated whenever getItems() is called.
var app = angular.module("testApp", []);
app.controller("mainCtrl", function($scope, $q) {
var that = this;
var deferred = $q.defer();
that.r = 0;
var i = [{ name: "toronto" }, { name: "chicago" }, { name: "new york" }];
var j = [{ name: "Martin" }, { name: "Stephanie" }, { name: "Kirk" }];
that.currentItems = i;
this.getItems = function () {
if (that.r % 2 == 0) {
console.log("getItems: i returned");
that.currentItems = i;
deferred.resolve(i);
} else {
console.log("getItems: j returned");
that.currentItems = j;
deferred.resolve(j);
}
return deferred.promise;
};
this.changeVal = function() {
that.r += 1;
this.getItems();
console.log(that.r);
};
});
app.directive("parentDirective", function() {
return {
template:
'<child-directive items="ct.currentItems"></child-directive><button ng-click="ct.changeVal()">Change Value</button>',
controller: "mainCtrl",
controllerAs: "ct",
scope: {}
};
});
app.controller("childCtrl", function($scope) {
$scope.name = "Child controller";
});
app.directive("childDirective", function() {
return {
template:
'<select ng-model="selectedAction" ng-options="x.name for x in items"><option value="">Select</option></select>',
controller: "childCtrl",
scope: {
items: "<"
}
};
});
Heres a plunker using your example
I have a component that looks like this:
var meadow = angular.module('meadow');
meadow.component('productIncome', {
bindings: {
product: '='
},
templateUrl: '/static/js/app/product-detail/product-income/product-income.html',
controller: function () {
var vm = this;
console.log(vm.product)
}
})
Now I want to make us of the product that's bound to the component, but it's telling me that "vm.product is undefined". I've tried using $onInit but it still produces the same results. For $onInit it did :
vm.$onInit = function(){
console.log(vm.product)
}
product-income.html
<product-income product="$ctrl.products.product"></product-income>
How can I get this working? Thanks
I made a working demo here:
var meadow = angular.module('meadow', []);
meadow = meadow.controller("abc", function($scope) {
$scope.products = {
product: "a"
}
});
meadow.component('productIncome', {
bindings: {
product: '='
},
template: "<div></div>",
controller: function() {
var vm = this;
vm.$onChanges = function() {
console.log("onchange", vm.product);
}
setTimeout(function() {
console.log("timeout", vm.product);
});
}
})
<script src="https://code.angularjs.org/1.6.3/angular.min.js"></script>
<div ng-app="meadow" ng-controller="abc">
<product-income product="products.product"></product-income>
</div>
So it seems that the order of the controllers are not sure, u should make a async to get the value, or use "$onChanges " to listen to the value.
I'm new to angularjs. In my webapp I'm trying to work with user contacts as follows.
SERVICE
app.service('Contacts', function ($http,$timeout,$q) {
return {
getData: function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
}
});
ContactsController, OtherControllers
$scope.contactsBook = {};
...
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
});
I found the above method somewhere in SO itself. I used it because I don't want to use separate module for Contacts.
I can get data at page load. I can update my contacts at server through ajax posts (from ContactsController). Now I only need a way to update(/refresh) the list automatically in all controllers. How can I achieve that.
I found these three links related but being a newbie I'm unable to figure my way out.
While it is understandable that you may not want to update your current architecture, it may be necessary to adjust your calls slightly if you want to be able to easily share data between controllers via a service.
One flexible approach is to store the data in your service and register watchers in each controller. This allows you to call the service update from one controller (the Contacts controller) and have the change be reflected in all consuming controllers. Note the service is mocked.
You can find the working plunker example here.
Contacts Service:
var app = angular.module('app', []);
app.service('contactsService', function ($http) {
var contacts = [];
return {
loadData: function() {
var mockGet = $q.defer();
var data = [
{ id: 1, name: 'Jack' },
{ id: 2, name: 'Jill' }
];
contacts = data;
mockGet.resolve(contacts);
return mockGet.promise;
},
retrieveNewData: function() {
var mockGet = $q.defer();
var data = [
{ id: 1, name: 'Jack' },
{ id: 2, name: 'Jill' },
{ id: 3, name: 'Bob' },
{ id: 4, name: 'Susan' }
];
contacts = data;
mockGet.resolve(contacts);
return mockGet.promise;
},
getContacts: function () {
return contacts;
}
}
});
Contacts Controller:
app.controller('ContactsCtrl', ['$scope', 'contactsService',
function ($scope, contactsService) {
var vm = this;
vm.contacts = [];
vm.loadData = loadData;
vm.retrieveNewData = retrieveNewData;
$scope.$watch(angular.bind(contactsService, function () {
return contactsService.getContacts();
}), function (newVal) {
vm.contacts = newVal;
});
function loadData() {
contactsService.loadData();
}
function retrieveNewData() {
contactsService.retrieveNewData();
}
}
]);
Other Controller:
app.controller('OtherCtrl', ['$scope', 'contactsService',
function($scope, contactsService) {
var vm = this;
vm.contacts = [];
$scope.$watch(angular.bind(contactsService, function () {
return contactsService.getContacts();
}), function (newVal) {
vm.contacts = newVal;
});
}
]);
You can do
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
$scope.$emit('contacts:updated', data);
});
And then, where you need to notify the controller about the update:
$rootScope.$on('contacts:updated', function(e, contacts) {
$scope.contacts = contacts;
});
Another approach
The service is holding the current contacts list
app.service('Contacts', function ($http,$timeout,$q) {
this.currentList = [];
this.getData = function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
});
In your controller:
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
Contacts.currentList = data;
});
In other controller:
controller('AnotherController', function($scope, Contacts) {
$scope.contacts = Contacts.currentList;
});
If you are going to return an object literal you will need to turn your .service() into a .factory() module . In this case I'll be using a service module .
Example
Your service .
app.service('Contacts', function ($http,$timeout,$q) {
var Contacts = this;
contacts.getData = function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
}
return Contacts;
});
You will then need to inject this server into your ContactsController .
app.controller('ContactsController', function(Contacts){
$scope.data = null;
$scope.init = function(){
Contacts.getData().then(function(response){
$scope.data = response;
})
}
})
now data can be used in dom
Example
<li ng-repeat="x in data">{{x.name}}</li>
I currently have an issue when I call ui-tinymce directive in a custom directive. The custom directive is used to load dynamically links from backend for tinymce advlink plugin (+ load tinymce options object associated with a key passed as an attribute to the directive).
Here is my controller :
module.controller('Ctrl', function ($scope) {
$scope.test = {
val: "gfsgfdgh"
};
});
Here is how I call the directive in HTML:
<tinymce-custom type="minimal" ng-model="test.val"></tinymce-custom>`
And here is my directive :
module.directive('tinymceCustom', function($location, TinyService, Module, GenerateurPage) {
return {
restrict: 'E',
replace: true,
require:"ngModel",
scope: {
ngModel: '='
},
link: function(scope, element, attrs, ngModel){
scope.loaded = {
modules: false,
pages: false,
tinymce: false
};
scope.tinyOptions = {};
var link_list = [];
var modules = [];
var pages = [];
Module.findByOrganisme({}, function (data) {
data.forEach(function(module) {
modules.push({title: module.libelle, value: "/modules/"+module.id});
});
link_list.push({title: "Modules", menu: modules});
scope.loaded.modules = true;
initTiny();
});
GenerateurPage.findByOrganisme({}, function(data) {
data.forEach(function(page) {
pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
});
link_list.push({title: "Pages", menu: pages});
scope.loaded.pages = true;
initTiny();
});
function initTiny() {
if (!scope.loaded.modules || !scope.loaded.pages) {
return false;
}
scope.tinyOptions = TinyService.options(attrs.type);
console.log(scope);
scope.tinyOptions.link_list = link_list;
scope.loaded.tinymce = true;
}
},
template: '<div ng-if="loaded.tinymce"><textarea ui-tinymce="tinyOptions" ng-model="ngModel"></textarea></div>'
};
});
The problem is that the model passed to ui-tinymce directive is not updated when changing the text with the editor, and the text in the editor is not updated when the model from the controller is changed... BUT, the initial ngModel value is passed to ui-tinymce directive, so I think that is the data binding that is broken. Tried to watch it with $watch but nothing happens.
I can't figure how to fix it so I'm now looking for some help...
Thx
Finaly fixed it changing the approach :
<textarea tinymce-custom="minimal" ng-model="myVar"></textarea >
The final directive :
module.directive('tinymceCustom', function($location, $compile, $q, TinyService, Module, GenerateurPage) {
return {
restrict: 'A',
priority:999,
terminal:true, // prevent lower priority directives to compile after it
scope: true,
require: ['?ngModel'],
link: function(scope, el, attrs) {
// default is basic template
var type = attrs.tinymceCustom ? attrs.tinymceCustom : 'basic';
function loadTinyOptions(name) {
var loaded = {
modules: false,
pages: false,
tinymce: false
};
var link_list = [];
var deferred = $q.defer();
var initTiny = function() {
if (!loaded.modules || !loaded.pages) {
return false;
}
var tinyOptions = TinyService.options(name);
tinyOptions.link_list = link_list;
deferred.resolve(tinyOptions);
};
Module.findByOrganisme({}, function (data) {
var modules = [];
data.forEach(function(module) {
modules.push({title: module.libelle, value: "/modules/"+module.id});
});
link_list.push({title: "Modules", menu: modules});
loaded.modules = true;
initTiny();
});
GenerateurPage.findByOrganisme({}, function(data) {
var pages = [];
data.forEach(function(page) {
pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
});
link_list.push({title: "Pages", menu: pages});
loaded.pages = true;
initTiny();
});
return deferred.promise;
}
loadTinyOptions(type).then(function(data) {
scope._tinyOptions = data;
el.removeAttr('tinymce-custom'); // necessary to avoid infinite compile loop
el.attr('ui-tinymce', '{{_tinyOptions}}');
$compile(el)(scope);
});
}
};
Hope this can help.
I've got directive and service in my app (declared in separate files):
Service:
(function(){
angular.module('core', [])
.factory('api', function() {
return {
serviceField: 100
};
})
})();
Directive:
(function(){
angular.module('ui', ['core'])
.directive('apiFieldWatcher', function (api) {
return {
restrict: 'E',
replace: true,
scope: true,
template: '<div>+{{apiField}}+</div>',
controller: function($scope) {
$scope.apiField = 0;
},
link: function (scope) {
scope.$watch(function(){return api.serviceField}, function(apiFld){
scope.apiField = apiFld;
});
}
}
});
})();
And in another separate file I have native model:
function Model() { this.fld = 0; }
Model.prototype.setFld = function(a) { this.fld = a; }
Model.prototype.getFld = function() { return this.fld; }
How can I bind (two way) my native this.fld field to value in my AngularJS service?
The solution is in using this code:
Model.prototype.setFld = function(a) {
this.fld = a;
injector.invoke(['$rootScope', 'api', function($rootScope, api){
api.setField(a);
$rootScope.$digest();
}]);
};
Model.prototype.getFldFromApi = function() {
var self = this;
injector.invoke(['api', function(api){
self.fld = api.getField();
}]);
};
http://plnkr.co/edit/nitAVuOtzGsdJ49H4uyl
i think it's bad idea to use $digest on $rootScope, so we can maybe use
var scope = angular.element( elementObject ).scope();
to get needed scope and call $digest for it