I am trying to display Google Maps inside a Partial View with JSON. I have already tried the code inside a normal view and it works perfectly fine.
I have the following:-
Partial View ShowMap.cshtml
#using Microsoft.Web.Helpers
<script src="~/Scripts/jquery-1.8.3.min.js"></script>
<div class="experienceRestrictedText">
#Maps.GetGoogleHtml("1, Redmond Way, Redmond, WA", width: "400", height: "400")
</div>
Index.cshtml (where the Partial view is launched)
$('.modal_link_map').on('click', function (e) {
$('.modal_part').show();
var id = $(this).attr('data-id');
var context = $('#tn_select').load('/Experience/ShowMap?id=' + id, function () {
initSelect(context);
});
e.preventDefault();
return false;
});
And the controller Action is as follows:-
public ActionResult ShowMap()
{
_ItemID = Convert.ToInt32(Request.QueryString["id"]);
viewModel.ExperienceViewModel.Experience = unitOfWork.ExperienceRepository.GetByID(_ItemID);
return PartialView(viewModel);
}
Do I need to include anything else for this map to work?
I am not familiar with the #Maps.GetGoogleHtml helper but I am afraid that somehow this helper is including the following script:
<script src="//maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
Except that this script cannot be loaded asynchronously because it uses document.write() to load the actual API. To make this work you should specify a callback parameter. Here's how your partial could look like:
<div class="experienceRestrictedText">
<script src="//maps.google.com/maps/api/js?sensor=false&callback=initialize" type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
var map = new google.maps.Map(document.getElementById("map"), { zoom: 14, center: new google.maps.LatLng(47.652437, -122.132424), mapTypeId: google.maps.MapTypeId['ROADMAP'] });
new google.maps.Geocoder().geocode({ address: '1, Redmond Way, Redmond, WA' }, function (response, status) {
if (status === google.maps.GeocoderStatus.OK) {
var best = response[0].geometry.location;
map.panTo(best);
new google.maps.Marker({ map: map, position: best });
}
});
}
</script>
<div class="map" id="map" style="width:400px; height:400px;"></div>
</div>
Notice how a callback parameter is passed to the http://maps.google.com/maps/api/js script and the actual initialization of the maps is done inside this callback.
Also notice that I have removed the jquery script from the partial. I guess that jQuery is already loaded in your layout because you seem to be using it to attach to a .on() handler.
As an alternative you could have included the <script src="//maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script> in your main view but once you load the partial, the
#Maps.GetGoogleHtml helper would have included it a second time.
Personally I am not a big fan of those kind of helpers because they are completely obscuring the underlying calls leaving you without much control and understanding of what's happening.
Related
This is the way I was sending my model to an angular controller scope.
c# Controller:
public class AreaMenuController : RootController
{
// GET: Menu
public PartialViewResult Index()
{
var mod = Modules.Instance.ModuleList.FirstOrDefault(m => m.Prefix.Equals(base.GetArea(), StringComparison.CurrentCultureIgnoreCase));
return PartialView("_AreaMenu", mod.ModuleMenus);
}
}
View cshtml:
#using Nuclei.Models
#model IEnumerable<Nuclei.Models.Menu>
<script type="text/javascript">
#{ var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); }
window.areaMenus = #Html.Raw(serializer.Serialize(Model));
</script>
<script type="text/javascript" src="#Url.Content("~/Scripts/AngularControllers/_AreaMenuController.js")"></script>
<div ng-controller="AreaMenuController as vm" ng-init="vm.initializeController()">
<div id="accordion" ng-class="accordian-menu" style="visibility: visible;">
<ul>
<li ng-repeat="menu in vm.areaMenus">
...
</li>
</ul>
</div>
</div>
Angular Js file:
var NucleiApp = angular.module('NucleiApp');
NucleiApp.controller('AreaMenuController', ['$scope', '$http', '$location', function ($scope, $http, $location) {
"use strict";
var vm = this;
vm.initializeController = function () {
vm.areaMenus = window.areaMenus;
}
}]);
Question 1: Is there a smoother way to send your c# model through to angular other than through global window object?
You can use an $http get from angular, however because this is processed client-side there is always a bit of lag before it gets displayed, because it needs to call the c# controller and get the data. So I'm reserving $http get for updates only.
The other other way I was thinking was to send the view a Json object straight off:
c# controller:
public class AreaMenusController : RootController
{
// GET: Menu
public PartialViewResult Index()
{
return PartialView("_AreaMenu", GetAreaMenus());
}
public JsonResult GetAreaMenus()
{
var mod = Modules.Instance.ModuleList.FirstOrDefault(m => m.Prefix.Equals(base.GetArea(), StringComparison.CurrentCultureIgnoreCase));
return Json(new { areaMenus = mod.ModuleMenus }, JsonRequestBehavior.AllowGet);
}
}
View cshtml:
#using System.Web.Mvc
#model JsonResult
<script type="text/javascript">
window.areaMenus = #Model;
</script>
Question 2: I'm not really sure how to initialize the #model at this point and send it through to the angular file and again, if there is a better option than javascripts global window object... open to suggestions!
We currently do this to bootstrap a set of data that is later updated by a call into a WebAPI.
The reason we do this is we have found cases where the data, when bootstrapped via an API call, was coming back too slowly, which gave a bad experience to our users.
In our razor view:
<html>
<head>
<script>
window.areaMenus = '#Html.Raw(Model.SerializedJsonString)';
</script>
</head>
</html>
Then when our angular app is Run(), we deserialize the data, and use it from there:
var app = angular.module('myApp', ["stuff"])
.run(["menuService", (menuService) => {
// deserialize the json string into my object
var areaMenus = angular.fromJson(window.areaMenus);
// do something with it
menuService.Load(areaMenus);
}]);
This'll get the data available to angular immediately without having to wait for a $http request to complete, which should address your issue.
My goal is to build AJAX, which will show details of specific Building when user clicks on specific link <a...>
My Controller
public IActionResult BuildingDetail(int id)
{
return PartialView("_BuildingDetailsPartial", _buildingRepository.GetById(id));
}
My view
#foreach (var employee in Model.employees)
{
...
<a id="LoadBuildingDetail" href="#LoadBuildingDetail" data-assigned-id="#employee.Office.BuildingId"
onclick="AssignButtonClicked(this)">#employee.Office.Name</a>
...
}
Place to show Details of Building when user clicks on link. So _BuildingDetailsPartial will render here.
<div id="BuildingDetail">
</div>
Scripts: Im stuck here. I need to load specific BuildingDetail based on passed id.
<script type="text/javascript">
$(document).ready(function () {
function AssignButtonClicked(buildingId) {
var BuildingId = $(buildingId).data('assigned-id');
}
$("#LoadBuildingDetail").click(function () {
$("#BuildingDetail").load("/employees/buildingdetail/", { id: AssignButtonClicked() }, );
});
})
</script>
The issue is due to the logic of the click handler in jQuery. You're attempting to call a function in the onclick attribute of the element which won't be accessible as it's defined inside the document.ready scope.
Also, you're trying to set the id property of the object you send in the request to a function which has no return value.
To fix this, remove the onclick attribute from the HTML you generate, and just read the data attribute from the element directly in the jQuery event handler before you send the AJAX request. Try this:
#foreach (var employee in Model.employees)
{
<a class="LoadBuildingDetail" href="#LoadBuildingDetail" data-assigned-id="#employee.Office.BuildingId">#employee.Office.Name</a>
}
<script type="text/javascript">
$(function () {
$(".LoadBuildingDetail").click(function () {
$("#BuildingDetail").load("/employees/buildingdetail/", {
id: $(this).data('assigned-id')
});
});
})
</script>
This is my part of code from view called index
<div class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
...
...
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$.ajax({
url: '#Url.Action("_BlogsGrid","Blog")' + '?country=' + selectedCountry,
sucess: function(xhr, data) {
console.log('sucess');
},
error: function (err) {
console.log(err);
}
});
});
</script>
Here is the controller action code
public ActionResult _BlogsGrid(string country)
{
var blogs = _blogService.GetBlogWithCountryName(country).ToList();
var blogsList = BlogMapper.ToBlogIndex(blogs);
return PartialView(blogsList);
}
and here is _BlogsGrid view
#model Blog.BlogsList
<div class="pull-right">
#Html.DropDownListFor(m => m.SelectedCountry, new SelectList(Model.CountriesList), "Select a country", new { #class = "form-control" })
</div>
<br />
<br />
#if (Model.Blogs.Count == 0)
{
<div class="box-group">
<h4>Sorry we couldn't find any blog related to the country you asked for.</h4>
#Html.DisplayText("Hello world!")
</div>
}
else
{
<div class="box-group" id="accordion">
#foreach (var blog in Model.Blogs)
{
#*Some code*#
}
</div>
}
Thing is when i load it first time everything works fine, the controllers method gets hit and all the blogs get loaded this is how the view looks
but when I select a country from dropdown list and there are no blogs matching that country
it hits the if condition(putting a breakpoint in view I check if if condition is being executed or else condition is being executed, and it goes through if condition) in view (which is a partial view)
but the content of "if" is not loaded in the browser.
I am still getting same content as before. Any idea why my content is not being updated?
Update:
<div id="grid" class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$.ajax({
url: '#Url.Action("_BlogsGrid","Blog")' + '?country=' + selectedCountry,
sucess: function (data) {
$('#grid').html(data);
},
error: function (err) {
console.log(err);
}
});
});
</script>
in browser response
But still my div is not updating.
As others suggested, you're not updating your div content. That's way yo don't notice a change when the AJAX call is completed.
To ease things, in addition to .ajax(), jQuery provides the .load() method, which automatically fed the returned content into the matched elements. So your javascript could look like so:
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$("div.box-body").load('#Url.Action("_BlogsGrid","Blog")', 'country=' + selectedCountry);
});
</script>
Your ajax success function does nothing with the server response. It just print success in the browser console.
You need to use the server response on the success function and replace the atual content of the page with the new content.
Your html response is the first argument from success function.
You can look at jquery documentantion for better undertanding (http://api.jquery.com/jquery.ajax/)
To replace the content of your page the success function should be like that:
sucess: function(data) {
$("div.box-body").html(data);
console.log('sucess');
},
When the page is created, Razor replace all # by their value to generated the whole HTML page. This is why in debug mode, your #Html.DisplayText is hit.
However, when you load the page, the if is taken into account, and if the condition is false, you don't see the inner HTML.
To update your DOM, you have to use the data parameter of the success ajax call. This parameter is automatically set by the return of your _BlogsGrid method.
You can see it by modifying your success callback. Note that the data parameter should be the first parameter
sucess: function(data) {
console.log(data);
},
You're not updating the data after the initial load. Your ajax call returns the html content of your partial view. You'd have to update it to the DOM manually. Give your container div an id.
<div id="blogs-grid" class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
And then in your ajax success callback:
sucess: function(data) {
$('#blogs-grid').html(data);
},
Now whatever content your ajax returned will be bound to the blogs-grid div
Hello i googled to find a solution to my problem, every link brought me to asp forms solution, or solutions that do not ask for form inputs, this problem has form inputs and i cant seem too find a link for help, what im asking for is simple: get the data from user input through models by ajax calls.
View (index.cshtml):
#model UIPractice.Models.ModelVariables
<!-- Use your own js file, etc.-->
<script src="~/Scripts/jquery-1.10.2.js" type="text/javascript"></script>
<div id="div1">
#Html.BeginForm(){ #Html.TextBoxFor(x => x.test1, new { style = "width:30px" })}
<button id="hello"></button>
</div>
<div id="result1"></div>
<script type="text/javascript">
//Job is to load partial view on click along with model's objects
$(document).ready(function () {
$('#hello').click(function () {
$.ajax({
type: 'POST',
url: '#Url.Action("HelloAjax", "Home")',
data: $('form').serialize(),
success: function (result) {
$('#result1').html(result);
}
});
});
});
Model (ModelVariables.cs):
public class ModelVariables
{
//simple string that holds the users text input
public string test1 { get; set; }
}
Controller (HomeController.cs):
// GET: Home
public ActionResult Index()
{
ModelVariables model = new ModelVariables();
return View(model);
}
public ActionResult HelloAjax(ModelVariables model)
{
ViewBag.One = model.test1;`
return PartialView("_MPartial", model);
}
Partial View (_MPartial.cshtml):
#model UIPractice.Models.ModelVariables
#{
<div>
<!--if code runs, i get a blank answer, odd...-->
#Model.test1
#ViewBag.One
</div>
}
So go ahead and copy/paste code to run, you will notice that you get nothing when you click the button, with user text input, odd...
I see a few problems.
Your code which use the Html.BeginForm is incorrect. It should be
#using(Html.BeginForm())
{
#Html.TextBoxFor(x => x.test1, new { style = "width:30px" })
<button id="hello">Send</button>
}
<div id="result1"></div>
This will generate the correct form tag.
Now for the javascript part, you need to prevent the default form submit behavior since you are doing an ajax call. You can use the jQuery preventDefault method for that.
$(document).ready(function () {
$('#hello').click(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '#Url.Action("HelloAjax", "Home")',
data: $('form').serialize(),
success: function (result) {
$('#result1').html(result);
}
,error: function (a, b, c) {
alert("Error in server method-"+c);
}
});
});
});
Also, you might consider adding your page level jquery event handlers inside the scripts section (Assuming you have a section called "scripts" which is being invoked in the layout with a "RenderSection" call after jQyery is loaded.
So in your layout
<script src="~/PathToJQueryOrAnyOtherLibraryWhichThePageLevelScriptUsesHere"></script>
#RenderSection("scripts", required: false)
and in your indidual views,
#section scripts
{
<script>
//console.log("This will be executed only after jQuery is loaded
$(function(){
//your code goes here
});
</script>
}
In this project one of our programmers have written this code.
.
.
.
<script type="text/javascript">
$(document).ready(function () {
var dataSource = new kendo.data.DataSource({
schema: {
data: "Data",
total: "Total"
},
transport: {
read: {
url: '#Url.Action("List", "Customer")',
dataType: "json",
type: "POST"
}
},
pageSize: 10,
serverPaging: true,
serverFiltering: true,
serverSorting: true
});
$("#listView").kendoListView({
dataSource: dataSource,
pageable: true,
template: kendo.template($("#customerTemplate").html())
});
$(".pager").kendoPager({
dataSource: dataSource
});
});
<script type="text/x-kendo-tmpl" id="customerTemplate">
<article>
**<h3>${CustomerNumber} ${FullName}</h3>
***<h3>${CustomerNumber} #Html.ActionLink(${CustomerNumber}, "Details", "Customer", new {id=${CustomerNumber}}, null)</h3>
<div class="details">
<span class="phone" itemprop="telephone">${Phone}</span>
<span class="email">${Email}</span>
</div>
<div class="clearfix"></div>
</article>
If you take a look at where the two stars are, that's the original code.
My task right now is to translate that code somewhow into a working code as the one where the three stars are. But, no matter how I try, I can't get the values from jquery thing (${CustomerNumber}, ${FullName}) and into the actionlink.
I can barely understand this (newbie, only three months with MVC), so please try and keep it simple for me if you can.
I actually tried to put this code where the article tag is and the call it as #fullname to no avail.
#string fullname = ${FullName}
I have tried to search SO and Google, but to be honest, I do not even know how to pose the question. Is this related to jquery or kendo? Is it even possible to achieve what I want?
Regards, S
Razor code is rendered server side therefore once the page is loaded any new razor code pulled via JavaScript won't render correctly.
There are ways around this though, one option would be to pull the template from the server so you could pre-render the view before it comes down and then let Kendo do it's mapping.
However, if you want to keep it all client-side, you could have a JS helper method which renders during the page load, in which you can run the razor code e.g.
<script type="text/javascript">
$(document).ready(function () {
function getCustomerUrl(linkText, customerNumber) {
var urlTemplate = '#Html.ActionLink("linkText", "Details", "Customer", new { id="customerNumber" }, null)';
return urlTemplate.replace('linkText', linkText).replace('customerNumber', customerNumber);
}
...
});
</script>
Then in your template simply call that method with the relevant parameters i.e.
<script type="text/x-kendo-tmpl" id="customerTemplate">
<article>
<h3>#= getCustomerUrl(FullName, CustomerNumber) #</h3>
<div class="details">
<span class="phone" itemprop="telephone">${Phone}</span>
<span class="email">${Email}</span>
</div>
<div class="clearfix"></div>
The short of it is you cannot do what you want to do. One thing to keep in mind is that all the code that is written in the view is processed on the server.
You are using javascript which gets processed on the client. The reason its not showing is your template has no idea what #Html.ActionLink(${CustomerNumber}, "Details", "Customer", new {id=${CustomerNumber}}, null) is. So you have a bit of a mismatch.
The question I would pose is, if this is working why do you need to change it?
Update base on comments
I would suggest this
<script type="text/javascript">
$(document).ready(function () {
var url = #Html.Action("Details", "Customer");
...
});
Then in your template this
<h3>${CustomerNumber} ${FullName}</h3>