I have two observablearrays in my ViewModel:
ShortlistedCountries
CompanyMainList
All companies names are displayed in a dropdown box. And the shortlisted companies are listed underneath it.
I would like to achieve two things from this demo.
Firstly, the users should be able to select the company name from the dropdown and add it to the Shortlisted company list.
Secondly, the users should get an error message (or alert) if they try to shortlist a company that has already been added to the shortlisted companies list.
Please have a look at my demo in JSFiddle
HTML
<div>
<div>All Companies</div>
<div>
<div id="rdoCompanyServer">
<select data-bind="options:CompanyMainList, optionsText:'CompanyName', optionsValue:'id', optionsCaption: 'Select a company...'"></select> Add to Shortlist
</div>
</div>
</div>
<br/>
<br/>
<br/>
<div>
<div id="sectionHeading">My Shortlisted Companies</div>
<div>
<div>
<ol data-bind="foreach: ShortlistedCountries">
<li><span data-bind="text:CompanyName"></span><span id="removeshortlist">
Remove</span>
</li>
</ol>
<br />
</div>
</div>
</div>
Knockout JS
function CompanyViewModel() {
var self = this;
self.currentDemoLicenses = ko.pureComputed(function () {
return self.demoLicenses().length;
});
/* adding bookmark servers in the same view TEST */
self.bookmarkedServerCount = ko.pureComputed(function () {
return self.ShortlistedCountries().length;
});
self.ShortlistedCountries = ko.observableArray([{
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}
]);
self.DeleteShortlistedCountries = function (ShortlistedCountries) {
self.ShortlistedCountries.remove(ShortlistedCountries);
};
self.AddToShortlistedCountries = function () {
self.ShortlistedCountries.push(self.ShortlistedCountries);
};
self.CompanyMainList = ko.observableArray([{
CompanyName: 'Acer',
id: 1
}, {
CompanyName: 'Toshiba',
id: 12
}, {
CompanyName: 'Sony',
id: 13
}, {
CompanyName: 'LG',
id: 14
}, {
CompanyName: 'HP',
id: 6
}, {
CompanyName: 'Hitachi',
id: 6
}, {
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}, {
CompanyName: 'Panasonic',
id: 7
}]);
};
$(document).ready(function () {
ko.applyBindings(new CompanyViewModel());
});
Have a look at my demo in JSFiddle
Please let me know if I am missing some thing or is there anything wrong with my code.
Thank you.
Kind regards.
Sid
Try something like this
ViewModel:
function CompanyViewModel() {
var self = this;
self.selectedCompany = ko.observable();//has dropdown selection
self.ShortlistedCompanies = ko.observableArray([{
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}
]);
var isExists = function (data) { //function checks for duplicates
var status = false;
ko.utils.arrayFirst(self.ShortlistedCompanies(), function (item) {
if (item.id === data.id) {
status = true;
return status;
}
});
return status;
}
self.DeleteShortlistedCompanies = function (ShortlistedCompanies) {
self.ShortlistedCompanies.remove(ShortlistedCompanies);
};
self.AddToShortlistedCompanies = function () {
if (!self.selectedCompany()) {
alert('select something');
return;
}
if (isExists(self.selectedCompany())) {
alert('Cannot add duplicates');
return;
}
self.ShortlistedCompanies.push(self.selectedCompany());
};
self.CompanyMainList = ko.observableArray([{
CompanyName: 'Acer',
id: 1
}, {
CompanyName: 'Toshiba',
id: 12
}, {
CompanyName: 'Sony',
id: 13
}, {
CompanyName: 'LG',
id: 14
}, {
CompanyName: 'HP',
id: 6
}, {
CompanyName: 'Hitachi',
id: 6
}, {
CompanyName: 'Apple Inc',
id: 11
}, {
CompanyName: 'TDK',
id: 15
}, {
CompanyName: 'Samsung',
id: 16
}, {
CompanyName: 'Panasonic',
id: 7
}]);
};
View :
<div id="rdoCompanyServer">
<select data-bind="options:CompanyMainList, optionsText:'CompanyName', optionsCaption: 'Select a company...',value:selectedCompany"></select> Add to Shortlist
</div>
For reference working fiddle here
Related
I keep getting these 'child' things in my Javascript collection after certain operations. The 'child' keyword is showing up in my terminal after logging the Javascript collection.
Whats strange is that I can't actually find good documentation anywhere online on what this means. Seems like it should be a basic concept.
When I do google it I just get a ton of results for 'child' in context of HTML and the DOM.
What does it mean in javascript? And how could I fix this collection to have these nested collections without the 'child' thing.
Gosh I wish I could speak about it with more sophistication :p
More Context on How This 'Bad' Collection is Generated
So I'm trying to populate JSON data from my Mongodb database and return it to the frontend. Essentially I have nested collections like so:
Institution
|
------------> memberOrganizations
|
---------------------> associatedVIPs
Where I'm originally grabbing Institutions I can populate collections one level down using built in populate functionality.
Doing like so:
Institution.find()
.populate('memberOrganizations')
.then(function (institutions) {
console.log("All institutions, memberOrganizations populated no problem.");
return res.json(institutions);
});
The problem is coming in when I try to go populate collections inside those member organizations, and replace existing memberOrganizations data with that.
Institution.find()
.populate('memberOrganizations')
.then(function (institutions) {
var populateOrganizationOrderManagers = _.map(institutions, function (institution) {
var masterInstitution = _.cloneDeep(institution);
return new Promise(function (resolve, reject) {
var ids = _.map(institution.memberOrganizations, 'id');
Organization.find(ids).populate('associatedVIPs').then(function (orgs) {
masterInstitution.memberOrganizations = orgs;
resolve(masterInstitution);
});
});
});
return Promise.all(populateOrganizationOrderManagers)
.then(function (institutionsWithOrderManagers) {
return res.json(institutionsWithOrderManagers);
});
})
Printouts of the JSON data using console.log to print to my terminal
(Simplified all data by a bit to make it easier to make a point)
What it looks like:
[ child {
memberOrganizations:
[ { associatedVIPs:
[ { firstName: 'Gregory',
lastName: 'Parker',
email: 'info#parker2018.com',
id: '5ab94183164475010026184b' } ],
institution: '5ab940b71644750100261845',
name: 'Greg Parker',
type: 'Student',
id: '5ab941401644750100261847' },
{ associatedVIPs:
[ { firstName: 'Irma',
lastName: 'Francisco',
email: 'irmaf#houstontransporter.com',
id: '5ae348da1ef63b245a74fe2d' } ],
institution: '5ab940b71644750100261845',
name: 'Transporter Inc',
type: 'Other',
id: '5ae3488d1ef63b2c8f74fe29' } ],
name: 'Corporate',
createdAt: 2018-03-26T18:49:27.955Z,
updatedAt: 2018-07-05T15:00:02.562Z,
id: '5ab940b71644750100261845' }
What I'd like it to look like:
{ memberOrganizations:
[ {
name: 'Tau Kappa Epsilon',
type: 'Greek - Fraternity',
institution: '5a3996d47bab3401001cc1bc',
id: '5a3ae7ebdfd69201001aa54d'
associatedVIPs:
[ { firstName: 'Irma',
lastName: 'Francisco',
email: 'irmaf#houstontransporter.com',
id: '5ae348da1ef63b245a74fe2d' },
{ firstName: 'Zach',
lastName: 'Cook',
email: 'zach#google.com',
id: '5ae348da1ef63b245a74f' } ]
},
{ name: 'Farmhouse',
type: 'Greek - Fraternity',
institution: '5a3996d47bab3401001cc1bc',
id: '5a4e71e806b97a01003bd313' } ],
name: 'Troy University',
createdAt: '2017-12-19T22:46:44.229Z',
updatedAt: '2018-07-05T15:18:03.182Z',
id: '5a3996d47bab3401001cc1bc' },
{ memberOrganizations:
[ { name: 'Alpha Epsilon Pi',
type: 'Greek - Fraternity',
institution: '5a4d534606b97a01003bd2f1',
id: '5a4f95c44ec7b6010025d2fb' },
{ name: 'Alpha Delta Chi',
type: 'Greek - Sorority',
institution: '5a4d534606b97a01003bd2f1',
id: '5a74a35e1981ef01001d0633' },
{ name: 'Phi Sigma Kappa',
type: 'Greek - Fraternity',
institution: '5a4d534606b97a01003bd2f1',
id: '5a7ba61821024e0100be67b7' } ],
name: 'University of Alabama',
createdAt: '2018-01-03T22:03:50.929Z',
updatedAt: '2018-07-05T15:18:03.182Z',
id: '5a4d534606b97a01003bd2f1' }
I am having a hard time doing an Angular filter to solve a problem as below.
The filter logic is as below:
1) If all listItem of that item has qtyLeft != 0, do not display that item
2) If any of the listItem of that item has qtyLeft == 0, display the item title as well as coressponding listItem that have qtyLeft == 0
Here's a basic example of my data structure, an array of items:
$scope.jsonList = [
{
_id: '0001',
title: 'titleA',
list: {
listName: 'listNameA',
listItem: [
{
name: 'name1',
qtyLeft: 0
},
{
name: 'name2',
qtyLeft: 0
},
]
}
},
{
_id: '0002',
title: 'titleB',
list: {
listName: 'listNameB',
listItem: [
{
name: 'name3',
qtyLeft: 2
},
{
name: 'name4',
qtyLeft: 0
},
]
}
},
{
_id: '0003',
title: 'titleC',
list: {
listName: 'listNameC',
listItem: [
{
name: 'name5',
qtyLeft: 2
},
{
name: 'name6',
qtyLeft: 2
},
]
}
},
]
Here is the final expected outcome:
<div ng-repeat="item in jsonList | filter: filterLogic">
<div> </div>
</div>
// final outcome
<div>
<div>Title: titleA, ListItem: Name1, Name2</div>
<div>Title: titleB, ListItem: Name4</div>
</div>
Created working Plunkr here. https://plnkr.co/edit/SRMgyRIU7nuaybhX3oUC?p=preview
Do not forget to include underscore.js lib in your project if you are going to use this directive.
<div ng-repeat="jsonItem in jsonList | showZeroElement track by $index">
<div>Title:{{ jsonItem.title}}, ListItem:<span ng-repeat="item in
jsonItem.list.listItem track by $index" ng-if="item.qtyLeft==0">
{{item.name}}</span>
</div>
</div>
And
app.filter('showZeroElement', function() {
return function(input) {
var items = []
angular.forEach(input, function(value, index) {
angular.forEach(value.list.listItem, function(val, i) {
var found = _.findWhere(items, {
'title': value.title
})
if (val.qtyLeft === 0 && found === undefined) {
items.push(value)
}
})
})
return items
}
})
I'm currently trying to implement the new CRM's Autocomplete in a CRM online 2016 environment.
I've used the code from Sample: Auto-complete in CRM controls and have verified that it works on the Account form and another custom entity that already exists. However, when I use it one 1 specific custom entity and any of its string fields, the autocomplete box does not appear.
Attempts:
Creating a new form
Creating a brand new text field for the autocomplete to run on
Validated that it's hitting ext.getEventSource().showAutoComplete(resultSet);
Validated that no errors are being thrown from my JS
Anyone have any ideas of what might possibly be wrong? I'm thinking it has something to do with my entity or the entity form instead of the code or the text field.
/** Sample JavaScript code to demonstrate the auto-completion feature.
This sample configures the auto-complete feature for the "Account Name"
field in the account form. */
function suggestAccounts() {
// List of sample account names to suggest
accounts = [
{ name: 'A. Datum Corporation', code: 'A01' },
{ name: 'Adventure Works Cycles', code: 'A02' },
{ name: 'Alpine Ski House', code: 'A03' },
{ name: 'Bellows College', code: 'A04' },
{ name: 'Best For You Organics Company', code: 'A05' },
{ name: 'Blue Yonder Airlines', code: 'A06' },
{ name: 'City Power & Light', code: 'A07' },
{ name: 'Coho Vineyard', code: 'A08' },
{ name: 'Coho Winery', code: 'A09' },
{ name: 'Coho Vineyard & Winery', code: 'A10' },
{ name: 'Contoso, Ltd.', code: 'A11' },
{ name: 'Contoso Pharmaceuticals', code: 'A12' },
{ name: 'Contoso Suites', code: 'A13' },
{ name: 'Consolidated Messenger', code: 'A14' },
{ name: 'Fabrikam, Inc.', code: 'A15' },
{ name: 'Fabrikam Residences', code: 'A16' },
{ name: 'First Up Consultants', code: 'A17' },
{ name: 'Fourth Coffee', code: 'A18' },
{ name: 'Graphic Design Institute', code: 'A19' },
{ name: 'Humongous Insurance', code: 'A20' },
{ name: 'Lamna Healthcare Company', code: 'A21' },
{ name: 'Litware, Inc.', code: 'A22' },
{ name: 'Liberty Delightful Sinful Bakery & Cafe', code: 'A23' },
{ name: 'Lucerne Publishing', code: 'A24' },
{ name: 'Margie Travel', code: 'A25' },
{ name: 'Munson Pickles and Preserves Farm', code: 'A26' },
{ name: 'Nod Publishers', code: 'A27' },
{ name: 'Northwind Electric Cars', code: 'A28' },
{ name: 'Northwind Traders', code: 'A29' },
{ name: 'Proseware, Inc.', code: 'A30' },
{ name: 'Relecloud', code: 'A31' },
{ name: 'School of Fine Art', code: 'A32' },
{ name: 'Southridge Video', code: 'A33' },
{ name: 'Tailspin Toys', code: 'A34' },
{ name: 'Trey Research', code: 'A35' },
{ name: 'The Phone Company', code: 'A36' },
{ name: 'VanArsdel, Ltd.', code: 'A37' },
{ name: 'Wide World Importers', code: 'A38' },
{ name: 'Wingtip Toys', code: 'A39' },
{ name: 'Woodgrove Bank', code: 'A40' }
];
var keyPressFcn = function (ext) {
try {
var userInput = Xrm.Page.getControl("name").getValue();
resultSet = {
results: new Array(),
commands: {
id: "sp_commands",
label: "Learn More",
action: function () {
// Specify what you want to do when the user
// clicks the "Learn More" link at the bottom
// of the auto-completion list.
// For this sample, we are just opening a page
// that provides information on working with
// accounts in CRM.
window.open("http://www.microsoft.com/en-us/dynamics/crm-customer-center/create-or-edit-an-account.aspx");
}
}
};
var userInputLowerCase = userInput.toLowerCase();
for (i = 0; i < accounts.length; i++) {
if (userInputLowerCase === accounts[i].name.substring(0, userInputLowerCase.length).toLowerCase()) {
resultSet.results.push({
id: i,
fields: [accounts[i].name]
});
}
if (resultSet.results.length >= 10) break;
}
if (resultSet.results.length > 0) {
ext.getEventSource().showAutoComplete(resultSet);
} else {
ext.getEventSource().hideAutoComplete();
}
} catch (e) {
// Handle any exceptions. In the sample code,
// we are just displaying the exception, if any.
console.log(e);
}
};
Xrm.Page.getControl("name").addOnKeyPress(keyPressFcn);
}
You must have a lookup control on the form in order for the autocomplete to render. It sounds weird but this is currently the best workaround. I set mine to not be visible. Note: this is any lookup field it does not matter the relationship you choose. Having the lookup field sets something on the form to load the missing libraries.
I spend most of my weekend trying to figure out a similar situation. I could describe at great lengths the events that led to this discovery, but I will spare you.
I assume that Microsoft is trying to not include resources where they do not see a configured need and that is why the back-end autocomplete files are missing (until you add in a requirement for them).
I am new to knockout and I have no idea why I am getting this message.
Unable to process binding "simpleGrid: function (){return gridViewModel }"
Message: gridViewModel is not defined;
library_customization.js
define(['services/logger'], function (logger) {
var title = 'Library Customization';
var vm = {
activate: activate,
title: title
};
return vm;
var initialData = [
{ name: "Well-Travelled Kitten", sales: 352, price: 75.95 },
{ name: "Speedy Coyote", sales: 89, price: 190.00 },
{ name: "Furious Lizard", sales: 152, price: 25.00 },
{ name: "Indifferent Monkey", sales: 1, price: 99.95 },
{ name: "Brooding Dragon", sales: 0, price: 6350 },
{ name: "Ingenious Tadpole", sales: 39450, price: 0.35 },
{ name: "Optimistic Snail", sales: 420, price: 1.50 }
];
var PagedGridModel = function (items) {
this.items = ko.observableArray(items);
this.addItem = function () {
this.items.push({ name: "New item", sales: 0, price: 100 });
};
this.sortByName = function () {
this.items.sort(function (a, b) {
return a.name < b.name ? -1 : 1;
});
};
this.jumpToFirstPage = function () {
this.gridViewModel.currentPageIndex(0);
};
this.gridViewModel = new ko.simpleGrid.viewModel({
data: this.items,
columns: [
{ headerText: "Item Name", rowText: "name" },
{ headerText: "Sales Count", rowText: "sales" },
{ headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } }
],
pageSize: 4
});
};
ko.applyBindings(new PagedGridModel(initialData));
function activate() {
logger.log(title + ' selected', null, title, true);
return true;
}
});
library_customization.html
<!DOCTYPE html>
<html id="libraryCust">
<head>
<title> Project</title>
<script type="text/javascript" src="../../Scripts/knockout-3.3.0.js
</script>
</head>
<body>
<section>
<h2 class="page-title" data-bind="text: title"></h2>
</section>
<div class='liveExample'>
<div data-bind='simpleGrid: gridViewModel'></div>
<!-- -->
<button data-bind='click: addItem'>
Add item
</button>
<button data-bind='click: sortByName'>
Sort by name
</button>
<button data-bind='click: jumpToFirstPage, enable: gridViewModel.currentPageIndex'>
Jump to first page
</button>
</div>
</body>
</html>
Check the fiddle: JS Fiddle
I think the main problem is that you exit your module earlier than planned in the code below:
var title = 'Library Customization';
var vm = {
activate: activate,
title: title
};
return vm;
/* your main code follows below but never executes */
So I moved these properties into the PagedGridModel constructor.
Functionality allows you to add/delete description, title and time for the event.
I can not deal with the duplication(cloning) of the object which is created through v-model = (event.name, event.description and event.date)
All works fine with the removing selected object, it works like this:
deleteEvent: function(index){
if(confirm('Are you sure?')) {
this.events.$remove(index);
}
}
Here's an example of my code for a application to adding and changing events.
var vm = new Vue({
el: '#events',
data:{
event: { name:'', description:'', date:'' },
events: []
},
ready: function(){
this.fetchEvents();
},
methods: {
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
this.$set('events', events);
},
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent($index)"
deleteEvent: function(index){
if(confirm('Вы точно хотите удалить данную запись?')) {
this.events.$%remove(index);
}
},
cloneItem: function(index) {
}
}
});
there full code
http://codepen.io/Monocle/pen/ojLYGx
I found undocumented built it extend function Vue.util.extend that is equivalent to jQuery's extend.
In this case, you can avoid the enumerating the object properties
cloneItem: function(index) {
this.events.push(Vue.util.extend({},this.events[index]));
}
Access the cloned object via this.events[index], and then use it's properties to create new object and push it into events array:
cloneItem: function(index) {
this.events.push({ name: this.events[index].name,
description: this.events[index].description,
date: this.events[index].date });
}
Demo: http://codepen.io/anon/pen/MajKZO