The various inputs on my page are bound via knockout to a view model, let's say a customer record. That's all working fine.
Now, I want to put a SELECT at the top of the page that contains a list of all customers. The user will pick a customer, the record will be fetched from the database, and the data will be bound to the view model.
My question concerns conditional styling of the items in that SELECT list. It will be bound to an array of customer objects. The Customer object definition has a function called hasExpired:
var Customer = function (id, name, expiryDate) {
this.id = id;
this.customerName = name;
this.expiryDate = expiryDate;
this.hasExpired = function() {
return this.expiryDate == null ? false : true;
};
};
The ViewModel, to which the inputs on the page are bound, looks like this:
function ViewModel() {
var self=this;
self.customerRegion = ko.observable(),
self.customerName = ko.observable(),
.
.
.
self.allCustomers = Customers, // Customers is an array of Customer objects
self.selectedCustomer = ko.observable()
}
This knockout binding works; the SELECT is correctly populated with the list of customers:
<select id="customerSelect"
data-bind="options: allCustomers,
optionsText: 'customerName',
value: selectedCustomer />
I want to style the individual OPTIONS, adding an "expired" class if appropriate.
The individual items in the Customers SELECT are not bound to the view model. The SELECT functions like a navigation menu. The options are bound to the customer objects in the allCustomers array.
How to tell knockout to consult the hasExpired property of the customer object bound to each OPTION, to determine whether that particular option should get the expired property?
I want the customer to remain in the Select list but to appear with strike-through formatting.
Does the SELECT require its own view model?
The options binding has a parameter (optionsAfterRender) that allows for additional processing of the options elements. See Note 2: Post-processing the generated options (via the linked documentation).
Unless I have misinterpreted the structure of your data models, all that is required is a callback
self.setOptionStyling = function(option, item) {
ko.applyBindingsToNode(option, {css: {expired: item.hasExpired()} }, item);
}
bound to the optionsAfterRender parameter:
<select id="customerSelect"
data-bind="options: allCustomers,
optionsText: 'customerName',
value: selectedCustomer,
optionsAfterRender: setOptionStyling" />
Where the expired css class is defined as:
.expired {
text-decoration: line-through;
}
Fiddle
Related
My top level state model looks like this:
{
listOfItems: [], // Item[]
selections: {
itemId: 0
}
}
The items list may contain 10 different shopping items.
When the user clicks on an item, it updates the selection, and my #Selector will rerun.
Action: Set Item Selection
#Action(Item.Select)
setState(
{ setState }: StateContext<ItemsModel>,
{ itemId }: Item.Select
) {
setState(patch({ selections: patch({ itemId }) }));
}
Selector: Select Current Item
#Selector()
static getSelectedItem(state: ItemModel): Item {
return state.itemList.find(i => i.itemId === state.selections.itemId);
}
Problem is: I have up to 20 actions to perform on the selected Item. This results in:
Lots of .find() lookups to find item in the original array (both selector and actions)
Actions to perform on the listOfItems are in the same place as those to perform on a specific Item
I would like to: Keep the array and selection in this state, but separate out the "selected item" into a new substate, where the child state's model can just be Item type. This way I can encapsulate all the actions on Item in a different place to actions on the Items[] array.
I'm not sure how to keep them in sync. I need to keep the 'selectedItem' state up to date when the selection itemId changes in the parent. I also need to make sure any mutations to the selectedItem are reflected in the original array in the parent.
This seems like it might be more of a fundamental problem with how you are trying to represent your application state. Have you thought of normalizing your list of items? Or at least using a key/value lookup object instead of an array? You wouldn't need to use the .find() to do your lookup and could access the key of the object via the unique id you are interested in. Let me know if that is of any help!
I've got a list of users and their role, and it lists all users in my SQL database. I wanna be able to select one of the users and change their role like in the pictures below. But I need help to get the "data" of the selected user.
I'm using MVC Identity if that matters.
Controller code who shows the list
var role2 = (from r in context.Roles where r.Name.Contains("Administrator") select r).FirstOrDefault();
var admins = context.Users.Where(x => x.Roles.Select(y => y.RoleId).Contains(role2.Id)).ToList();
var adminVM = admins.Select(user => new UserViewModel
{
Username = user.UserName,
RoleName = "Administrator"
}).ToList();
View code:
#foreach (var usera in Model.Administrators)
{
<td>#usera.Username</td>
<td>#usera.RoleName</td>
<td>(bootstrap modal button)</td>
bootstrap modal body:
Current Role: <strong>#usera.RoleName</strong>
Then I want to remove the "selected" user from their role and add to another.
The explanation of the question is wrong. You have mentioned "But I need help to get the "data" of the selected user". Later you again mentioned "Then I want to remove the "selected" user from their role and add to another". The thing is that you need to bind a onClick method and pass there USER ID. See my below code,
<td><button onClick="openModal(USERId)" class="btn btn-default"></button></td>
See here "openModal" is a user-defined method where I'm passing that user Id. So now when you click this, take that value and assign it in a hidden field and as you said it will open the edit modal. See my below code,
//This is my hidden field in HTML
<button Type="hidden" id="myHiddenField"/>
//Below is the script
function openModal(id)
{
$('#myHiddenField').val(id);
//And here you can open the modal
}
So now when you assign a new Role to the particular User and click on SAVE/UPDATE button, call another jQuery/ajax method like below,
$('#myUpdateBtn').click(function({
//Here you can fetch the hidden field value, that is nothing but your selected UserId
var UserId = $('#myHiddenField').val();
$.ajax(function(){
url:"pass your url here",
Type:"Put", //Pass your method type. Ex: Post, Put, etc
Data:{Id:UserId, RoleName:yourNewRoleId},
success:function(d){}
})
}))
I have a mysql database with the tables "deliverables", "tags" and "deliverables_has_tags". I want to link tags to a deliverable.
This is what I do in my javascript file:
<script type="text/javascript" language="javascript">
$(function () {
var object = {};
$.ajax({
type: "GET",
url: "/Deliverable/Tags",
dataType: "json",
success: function (data) {
object.tags = data;
}
});
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
$("#tags")
// don't navigate away from the field on tab when selecting an item
.bind("keydown", function (event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data("ui-autocomplete").menu.active) {
event.preventDefault();
}
})
.autocomplete({
minLength: 0,
source: function (request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(
object.tags, extractLast(request.term)));
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
});
</script>
I can add multiple tags in my textbox.
But now I want to save this in my repository.
In my Action method in controller:
repository.AddDeliverable(model.Title, model.Description, model.UsernameID, data, datatwo, model.VideoUrl, model.AfstudeerrichtingID, model.ProjectID);
Tags action:
public JsonResult Tags()
{
var data = (repository.GetTags()).ToArray();
return Json(data, JsonRequestBehavior.AllowGet);
}
In my repository:
public IQueryable<string> GetTags()
{
return from tag in entities.tags
orderby tag.tag_name
select tag.tag_name;
}
I have no clue how to save this in my database.
Can anybody help me?
If I correctly understood your question, you have implemented your tag handling as follows:
There is MVC action method that returns the view with input placeholder containing no data
The placeholder itself is probably input type=text with id=tags
On 'dom ready' you fire ajax request to retrieve your tags from database, json-serialized as array; when it arrives you store it to tags variable (no error handling(!))
At the same time you decorate your input with jqueryui autocomplete that reacts on user input and returns items from the tags variable
Since input already contains tags (comma separated), your filter is first letters of the last tag
So, you have a situation when user has input a few comma separated tags (probably some of them can be new) and now wants to save it to the database. For each input, if that is a known tag you have to store it to "deliverables_has_tags". If there is a new tag, you have to store it both to "tags" and "deliverables_has_tags".
Most common scenario would be having a 'Save' button to start saving process.
Let's analyze what you have to do in the process.
1) Button click
On button click you use js to convert your comma separated tags string
using logic like split(term) to the array, and serialize it. You can
do serialization using serializeArray and manually create JSON
object, or serialize the whole form using
$('#yourForm').serialize(). I would choose the first option
because that way I get more control over JSON format and avoid
problems with MVC default model binder.
2) Ajax call
When the JSON object is ready to be sent, you fire an ajax POST
request to your MVC POST action method. When you save state always
avoid GET because new versions of browsers can scan thru your page and
actively preload urls using GET requests. You don't want this here. Of
course, use your data as a data-parameter in the ajax call.
3) Action method
When the request arrives, you have to process it in your controller
using a new action method. Typically in this case you will have
something like public JsonResult SaveTags(SaveTagsModel saveTags) {
... } which saves tags using your repository and returns result that
says something like 'OK' or 'ERROR' (sth like
response.isSaved=true/false). Tricky part can be designing view model
according to your JSON object - this could help. And regarding
collections this could be valuable info.
When saving, use transaction to ensure everything is saved at once.
First check if each tag exists in the database and insert those who
don't exist. After that, check for each tag if there is appropriate
n-n relation in deliverables_has_tags and insert it if there isn't.
I believe that you should use same repository encapsulation for both
operations.
In the post action, include FormCollection collection as argument and gather your tags from that. There is no automatic way. You could implement some custom model binding, but that is probably not worth the effort.
I have just started to use ember.js. I have two models in my application. One that holds data and one that holds this data edited by user. I bind them using one-way binding.
App.ViewModel = Ember.Object.create({
title:'title',
text:'text',
)};
App.EditModel = Ember.Object.create({
titleBinding: Ember.Binding.oneWay('App.ViewModel.title'),
textBinding: Ember.Binding.oneWay('App.ViewModel.text'),
)};
I let a user edit the data in EditModel model. But if the user discard the changes I want to be able to set the values back to the state before editing, ie. to the values in ViewModel.
Is there a way to rebind those properties? Or to manualy rise change event on properties in ViewModel so EditModel gets updated? Or any other approach to my problem?
You could create a custom Mixin which handles the reset for a model, see http://jsfiddle.net/pangratz666/CjB4S/
App.Editable = Ember.Mixin.create({
startEditing: function() {
var propertyNames = this.get('propertyNames');
var props = this.getProperties.apply(this, propertyNames);
this.set('origProps', props);
},
reset: function() {
var props = this.get('origProps');
Ember.setProperties(this, props);
}
});
App.myModel = Ember.Object.create(App.Editable, {
propertyNames: ['title', 'text'],
title: 'le title',
text: 'le text'
});
And later in the views you just invoke the startEditing when you want to take a snapshot of the current values and reset when you want to reset to the previous snapshot of the values.
How do you set the userdata in the controller action. The way I'm doing it is breaking my grid. I'm trying a simple test with no luck. Here's my code which does not work. Thanks.
var dataJson = new
{
total =
page = 1,
records = 10000,
userdata = "{test1:thefield}",
rows = (from e in equipment
select new
{
id = e.equip_id,
cell = new string[] {
e.type_desc,
e.make_descr,
e.model_descr,
e.equip_year,
e.work_loc,
e.insp_due_dt,
e.registered_by,
e.managed_by
}
}).ToArray()
};
return Json(dataJson);
I don't think you have to convert it to an Array. I've used jqGrid and i just let the Json function serialize the object. I'm not certain that would cause a problem, but it's unnecessary at the very least.
Also, your user data would evaluate to a string (because you are sending it as a string). Try sending it as an anonymous object. ie:
userdata = new { test1 = "thefield" },
You need a value for total and a comma between that and page. (I'm guessing that's a typo. I don't think that would compile as is.)
EDIT:
Also, i would recommend adding the option "jsonReader: { repeatitems: false }" to your javascript. This will allow you to send your collection in the "rows" field without converting it to the "{id: ID, cell: [ data_row_as_array ] }" syntax. You can set the property "key = true" in your colModel to indicate which field is the ID. It makes it a lot simpler to pass data to the grid.