UI Router nested views for scope inheritance - angular-ui-router

I'm having trouble understanding nested views in UI Router nested views... more specifically what they look like...?
I need to understand it because I need my $scope property to be inherited.
I see this example in the docs
$stateProvider
.state('contacts', {
abstract: true,
url: '/contacts',
templateUrl: 'contacts.html',
controller: function($scope){
$scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
}
})
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html'
})
.state('contacts.detail', {
url: '/:id',
templateUrl: 'contacts.detail.html',
controller: function($scope, $stateParams){
$scope.person = $scope.contacts[$stateParams.id];
}
})
Then I see something like this in the docs
$stateProvider
.state('report',{
views: {
'filters': {
templateUrl: 'report-filters.html',
controller: function($scope){ ... controller stuff just for filters view ... }
},
'tabledata': {
templateUrl: 'report-table.html',
controller: function($scope){ ... controller stuff just for tabledata view ... }
},
'graph': {
templateUrl: 'report-graph.html',
controller: function($scope){ ... controller stuff just for graph view ... }
}
}
})
Its all a bit confusing to me and since I need $scope inheritance I need to nest my views - just not quite sure which example that it it.
UPDATE
Here are my states
$locationProvider.html5Mode(false);
$stateProvider
.state('search', {
abstract: true,
url: '/search',
controller: 'searchCtrl',
controllerAs: 'vm',
templateUrl: 'search/index.html'
})
.state('search.query', {
url: '/',
controller: 'searchCtrl',
controllerAs: 'vm',
templateUrl: 'search/query.html'
})
.state('search.results', {
url: '/?q',//for query parameter in url
controller: 'searchCtrl',
controllerAs: 'vm',
templateUrl: 'search/results.html'
});
$urlRouterProvider.otherwise('/');
What I'm trying to achieve with this is just a simple search form on query.html, once a user enters or selects a search term then it goes to search.html with another search form and results.
These are my templates, I believe I have them set up correctly but something is wrong because nothing displays.
/app/index.html
<div ui-view></div>
/app/search/index.html
<div ui-view></div>
/app/search/query.html
<form>
...
</form>
/app/search/results.html
<div ui-view></div>
<div>
...
</div>

First one is an example of a nested state which fulfills your requirement for inheriting the scope object. e.g state/sub-state-a, state/sub-state-b
The comment above the first snippet you took from the doc reads:
Shows prepended url, inserted template, paired controller, and
inherited $scope object.
The second example is a nested view where you can define multiple views per state and use each depending on your use-case.
From the docs:
Then each view in views can set up its own templates (template,
templateUrl, templateProvider), controllers (controller,
controllerProvider).

Related

Asp.net core controller function with return partial view with select2 not working and remote function validation is not firing in modal popup

Select2 is not working and remote validation is not firing, this is only happens when I convert the code to modal popup but if not everything is working properly. What Am I missing in my code? Any advise or help much appreciated.. Thank you
Here is my code the modal:
$('#tbProducts tbody').on('click', 'button', function () {
var data = productsTable.row($(this).parents('tr')).data();
//alert(data.id);
$.ajax({
url: '#Url.Action("Edit", "Products")',
type: 'GET',
data: { id: data.id },
success: function (result) {
$('#EditUnitModal .modal-content').html(result);
$('#EditUnitModal').modal()
}
});
});
Here is the controller edit code:
public async Task<IActionResult> Edit(int? id)
{
//code here
return PartialView("__Edit", product);
}
And here is my partial view __Edit code:
#model intPOS.Models.Master.ViewModel.ProductViewModel
//code here
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$(function () {
$('#Unit').select2({
theme: 'bootstrap4',
dropdownParent: $('#EditUnitModal')
})
$('#Category').select2({
theme: 'bootstrap4',
dropdownParent: $('#EditUnitModal')
})
})
</script>
}
And View model code:
[Display(Name = "Product Code"), Required]
[Remote("CheckProduct", "Products", AdditionalFields = "Id", ErrorMessage = "Product already exists.")]
public string ProductCode
{
get
{
return _productcode;
}
set
{
_productcode = value.Trim();
}
}
Sample screen for not firing validation and select2 is not working:
sections aren't allowed in partial views. You can still use modals and partial views via Ajax for edit forms but there is a small modification you need to do in order for this to work:
Include all the necessary scripts in your page (this is mandatory as sections aren't allowed in partial views).
In your javascript code add these lines in order to parse the new form via jquery validation unobtrusive and your select elements via Select2.
$('#tbProducts tbody').on('click', 'button', function () {
var data = productsTable.row($(this).parents('tr')).data();
//alert(data.id);
$.ajax({
url: '#Url.Action("Edit", "Products")',
type: 'GET',
data: { id: data.id },
success: function (result) {
$('#EditUnitModal .modal-content').html(result);
//Here we parse the new form via jquery validation unobtrusive.
$.validator.unobtrusive.parse($('#EditUnitModal .modal-content form')[0]);
//Here we initialize select2 for the selected elements.
$(".yourSelect2ElementClass").select2({//options...});
//Now we launch the modal.
$('#EditUnitModal').modal()
}
});
});
Don't forget to remove the section from your partial view and include your scripts in the containing view.

UI-Router state controller: Named vs Inline Function

I am attempting to have DRY states with UI-Router. I have a page service AlchemyPage which fetches a page through the API using $http.get
This works perfectly when I use it with an inline controller:
$stateProvider.state('home', {
url: '/',
controller: function($stateParams, $scope, AlchemyPage) {
AlchemyPage.load($stateParams, $scope);
}
});
$stateProvider.state('organization', {
url: '/package-organization/:page',
templateUrl: 'alchemy/page.html',
controller: function($stateParams, $scope, AlchemyPage) {
$scope.org = true;
AlchemyPage.load($stateParams, $scope);
}
});
I attempted to get rid of repetition and created a controller:
angular.module('App').controller('MainPageCtrl', [
'$scope',
'$stateParams',
'isOrganization',
'AlchemyPage',
function($scope,
$stateParams,
isOrganization,
AlchemyPage) {
$scope.org = isOrganization;
AlchemyPage.load($stateParams,$scope);
}
]);
However, when I implemented as below, the controller does not get called.
$stateProvider.state('home', {
url: '/',
templateUrl: 'alchemy/home.html',
controller: 'MainPageCtrl',
resolve: {
isOrganization: 'false'
}
});
$stateProvider.state('organization', {
url: '/package-organization/:page',
templateUrl: 'alchemy/page.html',
controller: 'MainPageCtrl',
resolve: {
isOrganization: 'true'
}
});
Using AngularJS v.1.6.4
Using UI-Router v.0.4.2
I resolved this issue by using params instead of resolve
Modified 'home' state:
$stateProvider.state('home', {
url: '/',
templateUrl: 'alchemy/home.html',
controller: 'MainPageCtrl'
});
The below resolve object no longer needed in either state due to changing logic in AlchemyPage resource:
resolve: {
isOrganization: 'false'
}
Revised state for organization:
$stateProvider.state('organization', {
url: '/package-organization/:page',
templateUrl: 'alchemy/page.html',
controller: 'MainPageCtrl',
params: {
topic: 'organization'
}
});
You can see in the above that the flag is now set for organization by passing in
params: {
topic: 'organization'
}
So here is the cleaned up controller:
App.controller('MainPagedCtrl', [
'$stateParams',
'$scope',
'AlchemyPage',
function($stateParams,
$scope,
AlchemyPage) {
AlchemyPage.load($stateParams,
$scope);
}
]);
With the above corrections, the named controller works properly. Resolve does work as expected, I was just using it improperly.

Persist state params when calling $state.go

There's a route param that represents the current site language, defined in the abstract app state.
angular.module("app", ["ui.router"]).config(["$stateProvider",
function($stateProvider) {
$stateProvider
.state('main', {
url: '/:lng',
abstract: true,
template: '<div ui-view></div>'
})
.state('main.index', {
url: '/',
template: '<span>Index</span>'
})
.state('main.gallery', {
url: '/gallery',
template: '<span>Gallery</span>',
})
}
]);
For now, everytime calling $state.go, I have to add the lng property to the route param list, like
$state.go('main.index', { lng: 'en' });
even though the current value of lng is en
Is there anyway to make the lng remain the same like the previous state unless we explicitly change it by passing it to the route param list?
You can add a default value to the lng param in your state definition:
.state('main', {
url: '/:lng',
abstract: true,
template: '<div ui-view></div>'
params: {
lng: 'en'
}
})
More detail can be found here

Template and controller is not working in stateprovider

Route:
Here template and controller is not working. Below is code:
(function() {
'use strict';
angular.module('signup', ['ui.router']).config(appConfig);
appConfig.$inject = ['$stateProvider', '$locationProvider'];
function appConfig($stateProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: true
});
$stateProvider
.state('signup', {
url: '/',
templateUrl: 'app/signup/signup.html',
controller: 'SignUpController',
controllerAs: 'vm'
});
}
})();
Controller:
(function() {
'use strict';
angular
.module('signup')
.controller('SignUpController', SignUpController);
function SignUpController() {
var vm = this;
vm.title = "signup";
console.log("signup controller");
}
})();
HTML here
<h1>{{vm.title}}</h1>
console and title is not displaying. I don't know what is wrong here.
Thank you
The issue was probably the absence of tag, which is required by angular ui-router to insert any template associated to a state.
Adding the same solved the issue.

Is there a better way to redirect through a state using ui-router?

Is there a better way to redirect through a state without using $urlRouterProvider? I want to redirect to elsewhere after going through the base.home state.
$stateProvider
.state('base', {
url: '/base',
abstract: true,
views: {
"mainbase": {
templateUrl: 'ng/apps/base/views/header.html',
controller: 'HeaderCtrl'
}
},
})
.state('base.home', {
url: '',
views: {
"baseHeaderView": {
template: 'base home, you should not be here',
controller: function($state) {
// Can this be accomplished better?
$state.go('base.elsewhere');
}
}
}
})
.state('base.elsewhere',{
url: '/elsewhere'
//I want to redirect to here after going through the base.home state
});
Mark your redirect state with an attribute, then add a global listener for $stateChangeStart and conditionally supersede the transition.
This does the redirection in place of the original transition, so 'base.home' isn't even activated temporarily. If you
app.run($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(evt, toState, params) {
if (toState.redirectTo) {
evt.preventDefault();
$state.go(toState.redirectTo, params);
}
});
}
app.config(function($stateProvider) {
$stateProvider.state('base', {
url: '/base',
abstract: true,
views: {
"mainbase": {
templateUrl: 'ng/apps/base/views/header.html',
controller: 'HeaderCtrl'
}
},
})
.state('base.home', {
url: '',
views: {
"baseHeaderView": {
template: 'base home, you should not be here',
controller: function($state) {
// Can this be accomplished better?
$state.go('base.elsewhere');
}
}
},
redirectTo: 'base.elsewhere'
})
.state('base.elsewhere',{
url: '/elsewhere'
//I want to redirect to here after going through the base.home state
});
});

Resources