Access UI element of other model - c#

In my ASP.NET MVC 4 Web application I have two Models:
Block that has a property Block LinkedBlock.
BlockCollection which contains multiple Block. Every Block instance in Block.LinkedBlock is guaranteed to be also in the BlockCollection.
Now, what I want to do is the following:
If a Block has a linked block it should get a onchange handler that sets the text of the linked block to the text of this block.
Now, in principle, this is pretty simple:
if (Model.LinkedBlock != null)
{
var onChange = string.Format("setText({0}, this.text);", linkedBlockId);
#Html.TextBoxFor(m => m.Text, new { onchange = onChange });
}
<script type="text/javascript" language="javascript">
function setText(id, text) {
$("#" + id).val(text);
}
But the problem is, that I have no idea how to get the correct HTML ID of the linked block.
How do I get it?

Just add any ID you want to your HTML helper when you're rendering linked blocks, like:
#Html.TextBoxFor(m => m.Text, new { id = blockId });
Where blockId is your unique ID, say the ID record has in the database. Later, you can reference this ID when you're constructing onChange handler call. something like:
var onChange = string.Format("setText({0}, this.text);", Model.LinkedBlock.ID);
If you provide more context I'd be able to give you more sample code.

In the Block Model you should have an Id property. Then on your view you can reference that Id.
For example:
class Block
{
public Block LinkedBlock { get; set; }
public int Id { get; set; }
}
Your view has to be strongly typed:
#model Models.Block
or the following depending on what your are doing:
#model Models.BlockCollection
Then change your code to (just add the reference to the linked block):
if (Model.LinkedBlock != null)
{
var onChange = string.Format("setText({0}, this.text);", Model.LinkedBlock.Id);
#Html.TextBoxFor(m => m.Text, new { onchange = onChange });
}
You obviously have to make sure all the linkedBlocks are on the page with their respective ids.
Does that help?

Related

How to setup gridview in mvc

I would like to setup gridview under MyTickets tab.
How can I set this view to have only tickets from username eg 'testuser' ?
In controller I have below code. Table Zgloszenia is my table where I storing all information about tickets (date,username, id etc)
public ActionResult MyTickets(Zgloszenia model)
{
if (Session["UserID"] != null)
{
test dg = new test();
var item = dg.Zgloszenia.Where(x => x.UsrUsera == model.UsrUsera).SingleOrDefault();
return View(item);
}
else
{
return RedirectToAction("Login");
}
}
In view I have this code:
#model IEnumerable<Webform.Models.Zgloszenia>
#{
ViewBag.Title = "MyTickets";
WebGrid grid = new WebGrid(Model);
}
<h2>MyTickets</h2>
#if (Session["UserID"] != null)
{
<div>
Welcome: #Session["Username"]<br />
</div>
}
#grid.GetHtml(columns: new[] {
grid.Column("Opis"),
grid.Column("Priorytet"),
grid.Column("Srodowisko"),
grid.Column("NumerTaska"),
grid.Column("test"),
grid.Column("Date")
})
When I log in to my app and click Tab "MyTicket" I'm receiving below error:
A data source must be bound before this operation can be performed.
How I can fix this issue and set up view properly ?
In your action you are selecting a single item, not a collection to enumerate. On the contrary, the WebGrid expects a collection as a data source, so the way you declared things on the view is fine.
To check if that is indeed the issue, simply remove SingleOrDefault call in your action. If your Where call returns at least one record, you should be able to see it on the page:
test dg = new test();
var items = dg.Zgloszenia.Where(x => x.UsrUsera == model.UsrUsera).ToList();
return View(items);
Are you working with Visual studio? If yes, you have to make a dataset. (local or online) You do not have a database at the moment so he saves it nowhere.

Load Partial View inside another Partial View

I have a PartialView (_Letra) that receives information from a Controller named Music ... this way
public ActionResult CarregarLetra(string id, string artista, string musica)
{
return PartialView("_Letra", ArtMus(artista, musica));
}
public ResultLetra ArtMus(string artista, string musica)
{
//Conteúdo do metodo[..]
var queryResult = client.Execute<ResultLetra>(request).Data;
return queryResult;
}
Until then, no problem. What happens is that now I need to pass other information to this same PartialView (_Letra). This information is in PartialView (_Cifra).
So I added the following lines in my Music controller
public ActionResult CarregarCifra(string id, string artista, string musica)
{
return PartialView("_Cifra", GetCifra(artista, musica));
}
public ResultChords GetCifra(string artista, string musica)
{
var cfrTest= new Cifra();
var cifra = new ResultChords();
cifra.chords = cfrTest.GetInfs(artista, musica);
return cifra;
}
Everything working so far, PartialView _Cifra receives the information
I searched and found that I could use in PartialView _Letra the Html.Partial to load my PartialView _Cifra, I did this way then
I added
<div class="item">
<div class="text-carousel carousel-content">
<div>
#Html.Partial("_Cifra", new letero.mus.Infra.Models.ResultChords());
</div>
</div>
</div>
Now it starts to complicate why, the return of this is null, I believe it is due to a new instance of ResultChords that I make in Html.Partial
I have already tried using a ViewBag also to transpose the information between Partials, but probably not correctly, due to the return being null as well.
I've already done a lot of research and I'm not getting the information I need for PartialView _Letra.
There is a better way not to use Html.Partial, or to use it properly, as I am not aware.
In _Letra use
#Html.Action("CarregarCifra", "Music", new { id=Model.Id, artista=Model.Artista, musica=Model.Musica });
if the variables are available on the model then you can pass them in; otherwise, make use of the Viewbag and set them in CarregarLetra
Are you always passing a new object to the second partial? You could just create it at the top of the new _Cifra partial.
_Cifra.cshtml
#{
var resultChords = new letero.mus.Infra.Models.ResultChords();
}

AngularJs Select Option binding Issue shared modal

I have an issue where between two pages I am sharing a modal, both pages are using the same angularJs version(v1.2.14), and both pages call the exact same directives (ui.select2). The select box inside of the modal works on one page, whilst on the other it simply stay as the default option.
As an fyi I have tried implementing the select box in different styles e.g. ng-repeat on the options, and not using the track by. This however results in the other pages selecting options to break. I can only ever get one page to work and the other to break.
The strange thing is that in the background the bound value is updating correctly:
<div class="col-md-10">
<select ui-select2="{width: '100%'}" class="form-control"
ng-model="Model.DocumentTypeId"
ng-options="documentType.DocumentTypeId as documentType.DocumentTypeDescription for documentType in Model.DocumentTypes track by documentType.DocumentTypeId">
<option value="">Select Document Type</option>
</select>
</div>
If you have any suggestions of why this is occurring it would be great.
Here is a heavily truncated view of the controller:
module Views.TMDocumentUpload {
export class DocumentUpload implements IDocumentUpload {
public static SetupDocumentUploadDialog = "onSetupDocumentUploadDialog";
public Init(model: DocumentUploadViewModel) {
var self = this;
if (self.$scope.Model.HideDocumentType || self.$scope.Model.DocumentTypeId == null) {
if (self.$scope.Model.DocumentTypes.length == 1) {
self.$scope.Model.DocumentTypeId = self.$scope.Model.DocumentTypes[0].DocumentTypeId;
}
}
}
constructor(public $scope: IDocumentUploadScope, $http: ng.IHttpService, $timeout: ng.ITimeoutService) {
$scope.isAllSelected = true;
$scope.ShareConfig = [];
$scope.Model.DisplayShareOptions = false;
$scope.Init = () => {
var self = this;
$scope.$on(DocumentUpload.SetupDocumentUploadDialog,
(e: ng.IAngularEvent, args?: Views.TMDocumentUpload.DocumentUploadViewModel) => {
self.$scope.Model = new DocumentUploadViewModel();
$http.get("/GetInitialModel")
.success(function (data: DocumentUploadViewModel) {
angular.extend(data, args);
self.Init(data);
});
});
};
}
}
DocumentUpload.$inject = ["$scope", "$http","$timeout"];
}
I have resolved the issue by removing ui-select2, it seems that this was causing some sort of conflict with another directive in my second page.

what is the correct place to put code that reoccurs on every page in MVC

In my mvc solution I was originally using a viewModel to hold an IEnumerable of SelectListItems. These would be used to populate a dropdownfor element like below
#Html.DropDownListFor(model => model.Type, Model.PrimaryTypeList, new { data_acc_type = "account", data_old = Model.Type, #class = "js-primary-account-type" })
the problem being that whenever I had to return this view, the list would need re-populating with something pretty heavy like the following:
if(!ModelState.IsValid){
using (var typeRepo = new AccountTypeRepository())
{
var primTypes = typeRepo.GetAccountTypes();
var primtype = primTypes.SingleOrDefault(type => type.Text == model.Type);
model.PrimaryTypeList =
primTypes
.Select(type => new SelectListItem()
{
Value = type.Text,
Text = type.Text
}).ToList();
}
return View(model);
}
It seemed silly to me to have to rewrite - or even re-call (if put into a method) the same code every postback. - the same applies for the ViewBag as i have about 6 controllers that call this same view due to inheritance and the layout of my page.
At the moment i'm opting to put the call actually in my razor. but this feels wrong and more like old-school asp. like below
#{
ViewBag.Title = "Edit Account " + Model.Name;
List<SelectListItem> primaryTypes = null;
using (var typeRepo = new AccountTypeRepository())
{
primaryTypes =
typeRepo.GetAccountTypes()
.Select(t => new SelectListItem()
{
Value = t.Text,
Text = t.Text
}).ToList();
}
#Html.DropDownListFor(model => model.Type, primaryTypes, new { data_acc_type = "account", data_old = Model.Type, #class = "js-primary-account-type" })
Without using something completely bizarre. would there be a better way to go about this situation?
UPDATE: While semi-taking onboard the answer from #Dawood Awan below. my code is somewhat better, still in the view though and i'm 100% still open to other peoples ideas or answers.
Current code (Razor and Controller)
public static List<SelectListItem> GetPrimaryListItems(List<AccountType> types)
{
return types.Select(t => new SelectListItem() { Text = t.Text, Value = t.Text }).ToList();
}
public static List<SelectListItem> GetSecondaryListItems(AccountType type)
{
return type == null?new List<SelectListItem>(): type.AccountSubTypes.Select(t => new SelectListItem() { Text = t.Text, Value = t.Text }).ToList();
}
#{
ViewBag.Title = "Add New Account";
List<SelectListItem> secondaryTypes = null;
List<SelectListItem> primaryTypes = null;
using (var typeRepo = new AccountTypeRepository())
{
var primTypes = typeRepo.GetAccountTypes();
primaryTypes = AccountController.GetPrimaryListItems(primTypes);
secondaryTypes = AccountController.GetSecondaryListItems(primTypes.SingleOrDefault(t => t.Text == Model.Type));
}
}
In practice, you need to analyse where you app is running slow and speed up those parts first.
For starters, take any code like that out of the view and put it back in the controller. The overhead of using a ViewModel is negligible (speed-wise). Better to have all decision/data-fetching code in the controller and not pollute the view (Views should only know how to render a particular "shape" of data, not where it comes from).
Your "Something pretty heavy" comment is pretty arbitary. If that query was, for instance, running across the 1Gb connections on an Azure hosted website, you would not notice or care that much. Database caching would kick in too to give it a boost.
Having said that, this really is just a caching issue and deciding where to cache it. If the data is common to all users, a static property (e.g. in the controller, or stored globally) will provide fast in-memory reuse of that static list.
If the data changes frequently, you will need to provide for refreshing that in-memory cache.
If you used IOC/injection you can specific a single static instance shared across all requests.
Don't use per-session data to store static information. That will slow down the system and run you out of memory with loads of users (i.e. it will not scale well).
If the DropDown Values don't change it is better to save in Session[""], then you can access in you View, controller etc.
Create a class in a Helpers Folder:
public class CommonDropDown
{
public string key = "DropDown";
public List<SelectListItem> myDropDownItems
{
get { return HttpContext.Current.Session[key] == null ? GetDropDown() : (List<SelectListItem>)HttpContext.Current.Session[key]; }
set { HttpContext.Current.Session[key] = value; }
}
public List<SelectListItem> GetDropDown()
{
// Implement Dropdown Logic here
// And set like this:
this.myDropDownItems = DropdownValues;
}
}
Create a Partial View in Shared Folder ("_dropDown.cshtml"):
With something like this:
#{
// Add Reference to this Folder
var items = Helpers.CommonDropDown.myDropDownItems;
}
#Html.DropDownList("ITems", items, "Select")
And then at the top of each page:
#Html.Partial("_dropDown.cshtml")

Persist data using JSON

I'm tryping to use JSON to update records in a database without a postback and I'm having trouble implementing it. This is my first time doing this so I would appreciate being pointed in the right direction.
(Explanation, irrelevant to my question: I am displaying a list of items that are sortable using a jquery plugin. The text of the items can be edited too. When people click submit I want their records to be updated. Functionality will be very similar to this.).
This javascript function creates an array of the objects. I just don't know what to do with them afterwards. It is called by the button's onClick event.
function SaveLinks() {
var list = document.getElementById('sortable1');
var links = [];
for (var i = 0; i < list.childNodes.length; i++) {
var link = {};
link.id = list.childNodes[i].childNodes[0].innerText;
link.title = list.childNodes[i].childNodes[1].innerText;
link.description = list.childNodes[i].childNodes[2].innerText;
link.url = list.childNodes[i].childNodes[3].innerText;
links.push(link);
}
//This is where I don't know what to do with my array.
}
I am trying to get this to call an update method that will persist the information to the database. Here is my codebehind function that will be called from the javascript.
public void SaveList(object o )
{
//cast and process, I assume
}
Any help is appreciated!
I have recently done this. I'm using MVC though it shouldn't be too different.
It's not vital but I find it helpful to create the contracts in JS on the client side and in C# on the server side so you can be sure of your interface.
Here's a bit of sample Javascript (with the jQuery library):
var item = new Item();
item.id = 1;
item.name = 2;
$.post("Item/Save", $.toJSON(item), function(data, testStatus) {
/*User can be notified that the item was saved successfully*/
window.location.reload();
}, "text");
In the above case I am expecting text back from the server but this can be XML, HTML or more JSON.
The server code is something like this:
public ActionResult Save()
{
string json = Request.Form[0];
var serializer = new DataContractJsonSerializer(typeof(JsonItem));
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(json));
JsonItem item = (JsonItem)serializer.ReadObject(memoryStream);
memoryStream.Close();
SaveItem(item);
return Content("success");
}
Hope this makes sense.
You don't use CodeBehind for this, you use a new action.
Your action will take an argument which can be materialized from your posted data (which, in your case, is a JavaScript object, not JSON). So you'll need a type like:
public class Link
{
public int? Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Url { get; set; }
}
Note the nullable int. If you have non-nullable types in your edit models, binding will fail if the user does not submit a value for that property. Using nullable types allows you to detect the null in your controller and give the user an informative message instead of just returning null for the whole model.
Now you add an action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DoStuff(IEnumerable<Link> saveList)
{
Repository.SaveLinks(saveList);
return Json(true);
}
Change your JS object to a form that MVC's DefaultModelBinder will understand:
var links = {};
for (var i = 0; i < list.childNodes.length; i++) {
links["id[" + i + "]"] = list.childNodes[i].childNodes[0].innerText;
links["title[" + i + "]"] = list.childNodes[i].childNodes[1].innerText;
links["description[" + i + "]"] = list.childNodes[i].childNodes[2].innerText;
links["url[" + i + "]"] = list.childNodes[i].childNodes[3].innerText;
}
Finally, call the action in your JS:
//This is where I don't know what to do with my array. Now you do!
// presumes jQuery -- this is much easier with jQuery
$.post("/path/to/DoStuff", links, function() {
// success!
},
'json');
Unfortunately, JavaScript does not have a built-in function for serializing a structure to JSON. So if you want to POST some JSON in an Ajax query, you'll either have to munge the string yourself or use a third-party serializer. (jQuery has a a plugin or two that does it, for example.)
That said, you usually don't need to send JSON to the HTTP server to process it. You can simply use an Ajax POST request and encode the form the usual way (application/x-www-form-urlencoded).
You can't send structured data like nested arrays this way, but you might be able to get away with naming the fields in your links structure with a counter. (links.id_1, links.id_2, etc.)
If you do that, then with something like jQuery it's as simple as
jQuery.post( '/foo/yourapp', links, function() { alert 'posted stuff' } );
Then you would have to restructure the data on the server side.

Categories

Resources